<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="generator" content="VuePress 2.0.0-beta.48" />
    <meta name="theme" content="VuePress Theme Hope" />
    <meta property="og:url" content="https://vuepress-theme-hope-v2-demo.mrhope.site/zh/interview/Redis.html"><meta property="og:site_name" content="主题演示"><meta property="og:title" content="Redis"><meta property="og:type" content="article"><meta property="og:locale" content="zh-CN"><meta property="og:locale:alternate" content="en-US"><meta property="article:tag" content="Redis"><title>Redis | 主题演示</title><meta name="description" content="vuepress-theme-hope 的演示">
    <style>
      :root {
        --bg-color: #fff;
      }

      html[data-theme="dark"] {
        --bg-color: #1d2025;
      }

      html,
      body {
        background-color: var(--bg-color);
      }
    </style>
    <script>
      const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
      const systemDarkMode =
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches;

      if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
        document.querySelector("html").setAttribute("data-theme", "dark");
      }
    </script>
    <link rel="stylesheet" href="/assets/style.dbbe025d.css">
    <link rel="modulepreload" href="/assets/app.e732cc94.js"><link rel="modulepreload" href="/assets/Redis.html.d810a669.js"><link rel="modulepreload" href="/assets/plugin-vue_export-helper.21dcd24c.js"><link rel="modulepreload" href="/assets/Redis.html.78427b11.js"><link rel="prefetch" href="/assets/home.html.d675c4b6.js"><link rel="prefetch" href="/assets/intro.html.684ef626.js"><link rel="prefetch" href="/assets/index.html.e8ffec66.js"><link rel="prefetch" href="/assets/slide.html.050a6f1e.js"><link rel="prefetch" href="/assets/disable.html.b507ecac.js"><link rel="prefetch" href="/assets/encrypt.html.762116d5.js"><link rel="prefetch" href="/assets/markdown.html.5acf7a9a.js"><link rel="prefetch" href="/assets/page.html.141b0147.js"><link rel="prefetch" href="/assets/index.html.75bf4c69.js"><link rel="prefetch" href="/assets/article10.html.f5ea4d77.js"><link rel="prefetch" href="/assets/article11.html.6339c214.js"><link rel="prefetch" href="/assets/article12.html.5ac86a8f.js"><link rel="prefetch" href="/assets/article9.html.666c37a0.js"><link rel="prefetch" href="/assets/home.html.6e361c52.js"><link rel="prefetch" href="/assets/index.html.2d9b62a7.js"><link rel="prefetch" href="/assets/slide.html.6b655fa0.js"><link rel="prefetch" href="/assets/article1.html.f835efef.js"><link rel="prefetch" href="/assets/article2.html.e4775195.js"><link rel="prefetch" href="/assets/article3.html.bfdf934f.js"><link rel="prefetch" href="/assets/article4.html.69bccee4.js"><link rel="prefetch" href="/assets/article5.html.c7ac5439.js"><link rel="prefetch" href="/assets/article6.html.880c0652.js"><link rel="prefetch" href="/assets/article7.html.2cae84db.js"><link rel="prefetch" href="/assets/article8.html.6c46837c.js"><link rel="prefetch" href="/assets/disable.html.f629efab.js"><link rel="prefetch" href="/assets/encrypt.html.58d61689.js"><link rel="prefetch" href="/assets/markdown.html.779c42bf.js"><link rel="prefetch" href="/assets/page.html.125d74f5.js"><link rel="prefetch" href="/assets/index.html.3db704fb.js"><link rel="prefetch" href="/assets/article10.html.b5d65d2d.js"><link rel="prefetch" href="/assets/article11.html.b2cf93ee.js"><link rel="prefetch" href="/assets/article12.html.94e1cf22.js"><link rel="prefetch" href="/assets/article9.html.90cf2a62.js"><link rel="prefetch" href="/assets/Docker.html.de1063e8.js"><link rel="prefetch" href="/assets/Java基础.html.6c0b0f5c.js"><link rel="prefetch" href="/assets/Java容器.html.77448ff5.js"><link rel="prefetch" href="/assets/JUC.html.76307785.js"><link rel="prefetch" href="/assets/JVM.html.0bb340ff.js"><link rel="prefetch" href="/assets/Linux.html.90ea1c3b.js"><link rel="prefetch" href="/assets/MySQL.html.73ae62bf.js"><link rel="prefetch" href="/assets/NIO与Netty.html.a73529b1.js"><link rel="prefetch" href="/assets/index.html.2a38d188.js"><link rel="prefetch" href="/assets/主流框架.html.f21f66df.js"><link rel="prefetch" href="/assets/分布式与高性能高可用.html.38ab4746.js"><link rel="prefetch" href="/assets/操作系统.html.76b600e3.js"><link rel="prefetch" href="/assets/智力题.html.ec368cec.js"><link rel="prefetch" href="/assets/系统设计.html.fb8f2074.js"><link rel="prefetch" href="/assets/计算机网络.html.1a7f7d0e.js"><link rel="prefetch" href="/assets/设计模式.html.cd48ccf5.js"><link rel="prefetch" href="/assets/article1.html.ca54de60.js"><link rel="prefetch" href="/assets/article2.html.a99d479f.js"><link rel="prefetch" href="/assets/article3.html.5a2797ee.js"><link rel="prefetch" href="/assets/article4.html.3ac5a509.js"><link rel="prefetch" href="/assets/article5.html.c09d20ba.js"><link rel="prefetch" href="/assets/article6.html.c9e59f52.js"><link rel="prefetch" href="/assets/article7.html.948e77cc.js"><link rel="prefetch" href="/assets/article8.html.28c3977f.js"><link rel="prefetch" href="/assets/404.html.8a909211.js"><link rel="prefetch" href="/assets/index.html.690b188a.js"><link rel="prefetch" href="/assets/index.html.6085d793.js"><link rel="prefetch" href="/assets/index.html.932078ca.js"><link rel="prefetch" href="/assets/index.html.bb629f30.js"><link rel="prefetch" href="/assets/index.html.92b2d70c.js"><link rel="prefetch" href="/assets/index.html.82840baf.js"><link rel="prefetch" href="/assets/index.html.85406aa8.js"><link rel="prefetch" href="/assets/index.html.7b9124b8.js"><link rel="prefetch" href="/assets/index.html.e1e2a548.js"><link rel="prefetch" href="/assets/index.html.a9a69593.js"><link rel="prefetch" href="/assets/index.html.5b51b72b.js"><link rel="prefetch" href="/assets/index.html.ec7f0295.js"><link rel="prefetch" href="/assets/index.html.e14e2286.js"><link rel="prefetch" href="/assets/index.html.e89216be.js"><link rel="prefetch" href="/assets/index.html.c0adb933.js"><link rel="prefetch" href="/assets/index.html.a45798a3.js"><link rel="prefetch" href="/assets/index.html.e99c516b.js"><link rel="prefetch" href="/assets/index.html.f6be7fb4.js"><link rel="prefetch" href="/assets/index.html.6812b391.js"><link rel="prefetch" href="/assets/index.html.4bae905e.js"><link rel="prefetch" href="/assets/index.html.0599248b.js"><link rel="prefetch" href="/assets/index.html.963b2596.js"><link rel="prefetch" href="/assets/index.html.bbd9778f.js"><link rel="prefetch" href="/assets/index.html.c6a7a8d4.js"><link rel="prefetch" href="/assets/index.html.13391f8f.js"><link rel="prefetch" href="/assets/index.html.4b0a0f3f.js"><link rel="prefetch" href="/assets/index.html.bbd06d0d.js"><link rel="prefetch" href="/assets/index.html.9a4d8710.js"><link rel="prefetch" href="/assets/index.html.dfdff039.js"><link rel="prefetch" href="/assets/index.html.cd21ad11.js"><link rel="prefetch" href="/assets/index.html.72b3c545.js"><link rel="prefetch" href="/assets/index.html.0262e65d.js"><link rel="prefetch" href="/assets/index.html.8905ca40.js"><link rel="prefetch" href="/assets/index.html.98e54f53.js"><link rel="prefetch" href="/assets/index.html.707c5120.js"><link rel="prefetch" href="/assets/index.html.daf3ede2.js"><link rel="prefetch" href="/assets/index.html.ae769b93.js"><link rel="prefetch" href="/assets/index.html.59a75e1b.js"><link rel="prefetch" href="/assets/index.html.efb1d101.js"><link rel="prefetch" href="/assets/index.html.898df6f8.js"><link rel="prefetch" href="/assets/index.html.6339ac7f.js"><link rel="prefetch" href="/assets/index.html.0ffdd311.js"><link rel="prefetch" href="/assets/index.html.454f52cf.js"><link rel="prefetch" href="/assets/index.html.d8a5ddde.js"><link rel="prefetch" href="/assets/index.html.3be06723.js"><link rel="prefetch" href="/assets/index.html.1d180388.js"><link rel="prefetch" href="/assets/index.html.337241b0.js"><link rel="prefetch" href="/assets/index.html.0be3cd3f.js"><link rel="prefetch" href="/assets/index.html.8e5505d4.js"><link rel="prefetch" href="/assets/index.html.ffdd1452.js"><link rel="prefetch" href="/assets/index.html.d3825f4c.js"><link rel="prefetch" href="/assets/home.html.ccae7996.js"><link rel="prefetch" href="/assets/intro.html.fd34d8e1.js"><link rel="prefetch" href="/assets/index.html.ba786069.js"><link rel="prefetch" href="/assets/slide.html.0df38877.js"><link rel="prefetch" href="/assets/disable.html.6465acbf.js"><link rel="prefetch" href="/assets/encrypt.html.f697c2c7.js"><link rel="prefetch" href="/assets/markdown.html.f534aa4c.js"><link rel="prefetch" href="/assets/page.html.d91d46f6.js"><link rel="prefetch" href="/assets/index.html.b66745bd.js"><link rel="prefetch" href="/assets/article10.html.a245f19a.js"><link rel="prefetch" href="/assets/article11.html.c7128e02.js"><link rel="prefetch" href="/assets/article12.html.abd25c27.js"><link rel="prefetch" href="/assets/article9.html.7bba6b9f.js"><link rel="prefetch" href="/assets/home.html.eb29b64c.js"><link rel="prefetch" href="/assets/index.html.a51898c1.js"><link rel="prefetch" href="/assets/slide.html.4db0d16b.js"><link rel="prefetch" href="/assets/article1.html.8aeaf420.js"><link rel="prefetch" href="/assets/article2.html.1b3af75b.js"><link rel="prefetch" href="/assets/article3.html.83bc62c6.js"><link rel="prefetch" href="/assets/article4.html.9c3e20ef.js"><link rel="prefetch" href="/assets/article5.html.a86dfd69.js"><link rel="prefetch" href="/assets/article6.html.1005b310.js"><link rel="prefetch" href="/assets/article7.html.e62a511b.js"><link rel="prefetch" href="/assets/article8.html.5d7e1f67.js"><link rel="prefetch" href="/assets/disable.html.9d48f34e.js"><link rel="prefetch" href="/assets/encrypt.html.69059b41.js"><link rel="prefetch" href="/assets/markdown.html.f8f89db4.js"><link rel="prefetch" href="/assets/page.html.f139635c.js"><link rel="prefetch" href="/assets/index.html.126e92a0.js"><link rel="prefetch" href="/assets/article10.html.f00bfbec.js"><link rel="prefetch" href="/assets/article11.html.4e4aea0e.js"><link rel="prefetch" href="/assets/article12.html.ed056ef8.js"><link rel="prefetch" href="/assets/article9.html.c3cb96a4.js"><link rel="prefetch" href="/assets/Docker.html.f45c96a5.js"><link rel="prefetch" href="/assets/Java基础.html.56267f30.js"><link rel="prefetch" href="/assets/Java容器.html.5139503a.js"><link rel="prefetch" href="/assets/JUC.html.ebe8f287.js"><link rel="prefetch" href="/assets/JVM.html.c448b42c.js"><link rel="prefetch" href="/assets/Linux.html.43840acf.js"><link rel="prefetch" href="/assets/MySQL.html.eba28d84.js"><link rel="prefetch" href="/assets/NIO与Netty.html.66d42188.js"><link rel="prefetch" href="/assets/index.html.f3cc61a2.js"><link rel="prefetch" href="/assets/主流框架.html.48ca457c.js"><link rel="prefetch" href="/assets/分布式与高性能高可用.html.a85c8cdb.js"><link rel="prefetch" href="/assets/操作系统.html.c2f0370c.js"><link rel="prefetch" href="/assets/智力题.html.4d751983.js"><link rel="prefetch" href="/assets/系统设计.html.c1542e9d.js"><link rel="prefetch" href="/assets/计算机网络.html.724e0903.js"><link rel="prefetch" href="/assets/设计模式.html.2465754d.js"><link rel="prefetch" href="/assets/article1.html.ac18bda1.js"><link rel="prefetch" href="/assets/article2.html.051f8867.js"><link rel="prefetch" href="/assets/article3.html.ebe7849b.js"><link rel="prefetch" href="/assets/article4.html.a4dbc976.js"><link rel="prefetch" href="/assets/article5.html.395a5403.js"><link rel="prefetch" href="/assets/article6.html.74c49cbf.js"><link rel="prefetch" href="/assets/article7.html.bf053e97.js"><link rel="prefetch" href="/assets/article8.html.7213c414.js"><link rel="prefetch" href="/assets/404.html.22aaa4fc.js"><link rel="prefetch" href="/assets/index.html.bd6ff77c.js"><link rel="prefetch" href="/assets/index.html.a7880d9d.js"><link rel="prefetch" href="/assets/index.html.f5f99f6a.js"><link rel="prefetch" href="/assets/index.html.561cd4cd.js"><link rel="prefetch" href="/assets/index.html.2b2206fe.js"><link rel="prefetch" href="/assets/index.html.ec6b7467.js"><link rel="prefetch" href="/assets/index.html.146ab8c4.js"><link rel="prefetch" href="/assets/index.html.f201f6e8.js"><link rel="prefetch" href="/assets/index.html.a1a6c5d4.js"><link rel="prefetch" href="/assets/index.html.61b33c23.js"><link rel="prefetch" href="/assets/index.html.8640e57f.js"><link rel="prefetch" href="/assets/index.html.c3d0fc1d.js"><link rel="prefetch" href="/assets/index.html.461f8cdf.js"><link rel="prefetch" href="/assets/index.html.1dea7bef.js"><link rel="prefetch" href="/assets/index.html.e6addda8.js"><link rel="prefetch" href="/assets/index.html.2638e7e9.js"><link rel="prefetch" href="/assets/index.html.5db2b8e8.js"><link rel="prefetch" href="/assets/index.html.cbff7fea.js"><link rel="prefetch" href="/assets/index.html.5ac21d40.js"><link rel="prefetch" href="/assets/index.html.c7acfbee.js"><link rel="prefetch" href="/assets/index.html.fb44feb1.js"><link rel="prefetch" href="/assets/index.html.d4e476c9.js"><link rel="prefetch" href="/assets/index.html.ec5764c9.js"><link rel="prefetch" href="/assets/index.html.d185c9f1.js"><link rel="prefetch" href="/assets/index.html.0867f7ab.js"><link rel="prefetch" href="/assets/index.html.2c3899c7.js"><link rel="prefetch" href="/assets/index.html.8a2aee35.js"><link rel="prefetch" href="/assets/index.html.130e4c85.js"><link rel="prefetch" href="/assets/index.html.7277b095.js"><link rel="prefetch" href="/assets/index.html.c074ddbd.js"><link rel="prefetch" href="/assets/index.html.e91a40c5.js"><link rel="prefetch" href="/assets/index.html.0b2b027d.js"><link rel="prefetch" href="/assets/index.html.6b602146.js"><link rel="prefetch" href="/assets/index.html.16d4af72.js"><link rel="prefetch" href="/assets/index.html.b8fc0dcc.js"><link rel="prefetch" href="/assets/index.html.9b3fcbd9.js"><link rel="prefetch" href="/assets/index.html.eeb89bf7.js"><link rel="prefetch" href="/assets/index.html.5114cd1e.js"><link rel="prefetch" href="/assets/index.html.b8354286.js"><link rel="prefetch" href="/assets/index.html.fcd579df.js"><link rel="prefetch" href="/assets/index.html.e743d320.js"><link rel="prefetch" href="/assets/index.html.b34f28e1.js"><link rel="prefetch" href="/assets/index.html.dd5dd927.js"><link rel="prefetch" href="/assets/index.html.7df41339.js"><link rel="prefetch" href="/assets/index.html.9de9b7ae.js"><link rel="prefetch" href="/assets/index.html.db158b25.js"><link rel="prefetch" href="/assets/index.html.fcab0b26.js"><link rel="prefetch" href="/assets/index.html.afada1db.js"><link rel="prefetch" href="/assets/index.html.8681058b.js"><link rel="prefetch" href="/assets/index.html.1c990d16.js"><link rel="prefetch" href="/assets/index.html.9563932e.js"><link rel="prefetch" href="/assets/404.4ba82410.js"><link rel="prefetch" href="/assets/Layout.67323a43.js"><link rel="prefetch" href="/assets/Slide.be972901.js"><link rel="prefetch" href="/assets/Blog.caaad5e4.js"><link rel="prefetch" href="/assets/giscus.es.63137db5.js"><link rel="prefetch" href="/assets/auto.esm.36809f22.js"><link rel="prefetch" href="/assets/index.daae04bf.js"><link rel="prefetch" href="/assets/index.1842ee54.js"><link rel="prefetch" href="/assets/mermaid.esm.min.ee1e0284.js"><link rel="prefetch" href="/assets/highlight.esm.d982e650.js"><link rel="prefetch" href="/assets/markdown.esm.832a189d.js"><link rel="prefetch" href="/assets/math.esm.a3f84b6f.js"><link rel="prefetch" href="/assets/notes.esm.3c361cb7.js"><link rel="prefetch" href="/assets/reveal.esm.b96f05d8.js"><link rel="prefetch" href="/assets/search.esm.80da4a02.js"><link rel="prefetch" href="/assets/zoom.esm.8514a202.js"><link rel="prefetch" href="/assets/photoswipe.esm.092fbc15.js">
  </head>
  <body>
    <div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="skip-link sr-only">Skip to content</a><!--]--><div class="theme-container has-toc"><!--[--><!--[--><header class="navbar"><div class="navbar-left"><button class="toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!----><a href="/zh/" class="brand"><img class="logo" src="/logo.svg" alt="主题演示"><!----><span class="site-name hide-in-pad">主题演示</span></a><!----></div><div class="navbar-center"><!----><nav class="nav-links"><div class="nav-item hide-in-mobile"><a href="/zh/" class="nav-link" aria-label="博客主页"><span class="icon iconfont icon-home" style=""></span>博客主页<!----></a></div><div class="nav-item hide-in-mobile"><a href="/zh/home.html" class="nav-link" aria-label="项目主页"><span class="icon iconfont icon-home" style=""></span>项目主页<!----></a></div><div class="nav-item hide-in-mobile"><a href="/zh/guide/" class="nav-link" aria-label="使用指南"><span class="icon iconfont icon-creative" style=""></span>使用指南<!----></a></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="博文"><span class="title"><span class="icon iconfont icon-edit" style=""></span>博文</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><h4 class="dropdown-subtitle"><span>文章 1-4</span></h4><ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/zh/posts/article/article1.html" class="nav-link" aria-label="文章 1"><span class="icon iconfont icon-edit" style=""></span>文章 1<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article2.html" class="nav-link" aria-label="文章 2"><span class="icon iconfont icon-edit" style=""></span>文章 2<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article3.html" class="nav-link" aria-label="文章 3"><span class="icon iconfont icon-edit" style=""></span>文章 3<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article4.html" class="nav-link" aria-label="文章 4"><span class="icon iconfont icon-edit" style=""></span>文章 4<!----></a></li></ul></li><li class="dropdown-item"><h4 class="dropdown-subtitle"><span>文章 5-12</span></h4><ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/zh/posts/article/article5.html" class="nav-link" aria-label="文章 5"><span class="icon iconfont icon-edit" style=""></span>文章 5<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article6.html" class="nav-link" aria-label="文章 6"><span class="icon iconfont icon-edit" style=""></span>文章 6<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article7.html" class="nav-link" aria-label="文章 7"><span class="icon iconfont icon-edit" style=""></span>文章 7<!----></a></li><li class="dropdown-subitem"><a href="/zh/posts/article/article8.html" class="nav-link" aria-label="文章 8"><span class="icon iconfont icon-edit" style=""></span>文章 8<!----></a></li></ul></li><li class="dropdown-item"><a href="/zh/posts/article9.html" class="nav-link" aria-label="文章 9"><span class="icon iconfont icon-edit" style=""></span>文章 9<!----></a></li><li class="dropdown-item"><a href="/zh/posts/article10.html" class="nav-link" aria-label="文章 10"><span class="icon iconfont icon-edit" style=""></span>文章 10<!----></a></li><li class="dropdown-item"><a href="/zh/posts/article11.html" class="nav-link" aria-label="文章 11"><span class="icon iconfont icon-edit" style=""></span>文章 11<!----></a></li><li class="dropdown-item"><a href="/zh/posts/article12.html" class="nav-link" aria-label="文章 12"><span class="icon iconfont icon-edit" style=""></span>文章 12<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><a href="https://vuepress-theme-hope.github.io/v2/zh/" rel="noopener noreferrer" target="_blank" aria-label="主题文档" class="nav-link"><span class="icon iconfont icon-note" style=""></span>主题文档<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span><!----></a></div></nav><!----></div><div class="navbar-right"><!----><div class="nav-item"><div class="dropdown-wrapper i18n-dropdown"><button class="dropdown-title" type="button" aria-label="选择语言"><!--[--><svg xmlns="http://www.w3.org/2000/svg" class="icon i18n-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="i18n icon" style="width:1rem;height:1rem;vertical-align:middle;"><path d="M379.392 460.8 494.08 575.488l-42.496 102.4L307.2 532.48 138.24 701.44l-71.68-72.704L234.496 460.8l-45.056-45.056c-27.136-27.136-51.2-66.56-66.56-108.544h112.64c7.68 14.336 16.896 27.136 26.112 35.84l45.568 46.08 45.056-45.056C382.976 312.32 409.6 247.808 409.6 204.8H0V102.4h256V0h102.4v102.4h256v102.4H512c0 70.144-37.888 161.28-87.04 210.944L378.88 460.8zM576 870.4 512 1024H409.6l256-614.4H768l256 614.4H921.6l-64-153.6H576zM618.496 768h196.608L716.8 532.48 618.496 768z"></path></svg><!--]--><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/" class="nav-link" aria-label="English"><!---->English<!----></a></li><li class="dropdown-item"><a aria-current="page" href="/zh/interview/Redis.html" class="router-link-active router-link-exact-active nav-link active" aria-label="简体中文"><!---->简体中文<!----></a></li></ul></button></div></div><div class="nav-item"><a class="repo-link" href="https://gitee.com/changluJava/personal-reading" target="_blank" rel="noopener noreferrer" aria-label="Gitee"><svg xmlns="http://www.w3.org/2000/svg" class="icon gitee-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="gitee icon" style="width:1.25rem;height:1.25rem;vertical-align:middle;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"></path></svg></a></div><div class="nav-item hide-in-mobile"><button id="appearance-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="light icon" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><!----><!----><button class="toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span class="button-container"><span class="button-top"></span><span class="button-middle"></span><span class="button-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow left"></span></div><aside class="sidebar"><!--[--><!----><!--]--><ul class="sidebar-links"><li><!--[--><a href="/zh/" class="nav-link sidebar-link sidebar-page" aria-label="博客主页"><span class="icon iconfont icon-home" style=""></span>博客主页<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/home.html" class="nav-link sidebar-link sidebar-page" aria-label="项目主页"><span class="icon iconfont icon-home" style=""></span>项目主页<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/slide" class="nav-link sidebar-link sidebar-page" aria-label="幻灯片"><!---->幻灯片<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><section class="sidebar-group"><button class="sidebar-heading clickable active"><span class="icon iconfont icon-creative" style=""></span><a href="/zh/interview/" class="title">面试指南</a><span class="arrow down"></span></button><ul class="sidebar-links"><li><!--[--><a href="/zh/interview/Docker.html" class="nav-link sidebar-link sidebar-page" aria-label="Docker"><span class="icon iconfont icon-lock" style=""></span>Docker<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Java%E5%9F%BA%E7%A1%80.html" class="nav-link sidebar-link sidebar-page" aria-label="JAVA"><span class="icon iconfont icon-lock" style=""></span>JAVA<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Java%E5%AE%B9%E5%99%A8.html" class="nav-link sidebar-link sidebar-page" aria-label="Java容器"><span class="icon iconfont icon-lock" style=""></span>Java容器<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/JUC.html" class="nav-link sidebar-link sidebar-page" aria-label="JUC"><span class="icon iconfont icon-lock" style=""></span>JUC<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/JVM.html" class="nav-link sidebar-link sidebar-page" aria-label="JVM"><span class="icon iconfont icon-lock" style=""></span>JVM<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/Linux.html" class="nav-link sidebar-link sidebar-page" aria-label="Linux"><span class="icon iconfont icon-lock" style=""></span>Linux<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/MySQL.html" class="nav-link sidebar-link sidebar-page" aria-label="MySQL"><span class="icon iconfont icon-lock" style=""></span>MySQL<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/NIO%E4%B8%8ENetty.html" class="nav-link sidebar-link sidebar-page" aria-label="Netty"><span class="icon iconfont icon-lock" style=""></span>Netty<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a aria-current="page" href="/zh/interview/Redis.html" class="router-link-active router-link-exact-active nav-link active sidebar-link sidebar-page active" aria-label="Redis"><span class="icon iconfont icon-lock" style=""></span>Redis<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis基础" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、Redis基础"><!---->1、Redis基础<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、简单介绍一下-redis" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、简单介绍一下 Redis!"><!---->1、简单介绍一下 Redis!<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、分布式缓存常见的技术选型方案有哪些" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、分布式缓存常见的技术选型方案有哪些？"><!---->2、分布式缓存常见的技术选型方案有哪些？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、说一下-redis-和-memcached-的区别和共同点" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、说一下 Redis 和 Memcached 的区别和共同点"><!---->3、说一下 Redis 和 Memcached 的区别和共同点<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、缓存数据的处理流程" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、缓存数据的处理流程"><!---->4、缓存数据的处理流程<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_5、为什么要用-redis-为什么要用缓存" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、为什么要用 Redis/为什么要用缓存？"><!---->5、为什么要用 Redis/为什么要用缓存？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_6、-redis-除了做缓存-还能做什么" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="6、 Redis 除了做缓存，还能做什么？"><!---->6、 Redis 除了做缓存，还能做什么？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_7、redis-可以做消息队列么" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="7、Redis 可以做消息队列么？"><!---->7、Redis 可以做消息队列么？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_8、redis的应用场景有哪些" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="8、Redis的应用场景有哪些？"><!---->8、Redis的应用场景有哪些？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_9、redis命令" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="9、Redis命令"><!---->9、Redis命令<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_10、pipeline的作用" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="10、pipeline的作用？"><!---->10、pipeline的作用？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_11、lua脚本-场景-限流" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="11、LUA脚本（场景：限流）"><!---->11、LUA脚本（场景：限流）<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_12、什么是redlock" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="12、什么是RedLock？"><!---->12、什么是RedLock？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_13、jedis-与-redisson-对比有什么优缺点" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="13、Jedis 与 Redisson 对比有什么优缺点？"><!---->13、Jedis 与 Redisson 对比有什么优缺点？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、redis-常见数据结构" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、Redis 常见数据结构"><!---->2、Redis 常见数据结构<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、基本的和特殊的数据类型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、基本的和特殊的数据类型"><!---->1、基本的和特殊的数据类型<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、sortedset和list异同点" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、SortedSet和List异同点？"><!---->2、SortedSet和List异同点？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、redis-线程模型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、Redis 线程模型"><!---->3、Redis 线程模型<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis-单线程模型了解" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、Redis 单线程模型了解"><!---->1、Redis 单线程模型了解<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、redis6-0-之前为什么不使用多线程" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、Redis6.0 之前为什么不使用多线程？"><!---->2、Redis6.0 之前为什么不使用多线程？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、redis6-0-之后为何引入了多线程" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、Redis6.0 之后为何引入了多线程？"><!---->3、Redis6.0 之后为何引入了多线程？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、redis-内存管理" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、Redis 内存管理"><!---->4、Redis 内存管理<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis-给缓存数据设置过期时间有啥用" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、Redis 给缓存数据设置过期时间有啥用？"><!---->1、Redis 给缓存数据设置过期时间有啥用？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、-redis-是如何判断数据是否过期的呢" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、 Redis 是如何判断数据是否过期的呢？"><!---->2、 Redis 是如何判断数据是否过期的呢？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、过期的数据的删除策略了解么" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、过期的数据的删除策略了解么？"><!---->3、过期的数据的删除策略了解么？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、redis-内存淘汰机制了解么" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、Redis 内存淘汰机制了解么？"><!---->4、Redis 内存淘汰机制了解么？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_5、redis-持久化机制" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、Redis 持久化机制"><!---->5、Redis 持久化机制<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、怎么保证-redis-挂掉之后再重启数据可以进行恢复" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、怎么保证 Redis 挂掉之后再重启数据可以进行恢复？"><!---->1、怎么保证 Redis 挂掉之后再重启数据可以进行恢复？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、什么是-rdb-持久化" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、什么是 RDB 持久化？"><!---->2、什么是 RDB 持久化？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、什么是-aof-持久化" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、什么是 AOF 持久化？"><!---->3、什么是 AOF 持久化？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、rdb与aof如何选择" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、RDB与AOF如何选择？"><!---->4、RDB与AOF如何选择？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_5、aof-重写了解吗" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、AOF 重写了解吗？"><!---->5、AOF 重写了解吗？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_6、redis-4-0-对于持久化机制做了什么优化" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="6、Redis 4.0 对于持久化机制做了什么优化？"><!---->6、Redis 4.0 对于持久化机制做了什么优化？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_6、redis事务" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="6、Redis事务"><!---->6、Redis事务<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、如何使用-redis-事务" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、如何使用 Redis 事务？"><!---->1、如何使用 Redis 事务？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、redis-支持原子性吗" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、Redis 支持原子性吗？"><!---->2、Redis 支持原子性吗？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、如何解决-redis-事务的缺陷" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、如何解决 Redis 事务的缺陷？"><!---->3、如何解决 Redis 事务的缺陷？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_7、redis-性能优化" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="7、Redis 性能优化"><!---->7、Redis 性能优化<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis-bigkey" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、Redis bigkey"><!---->1、Redis bigkey<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、大量-key-集中过期问题" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、大量 key 集中过期问题"><!---->2、大量 key 集中过期问题<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_8、redis-生产问题" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="8、Redis 生产问题"><!---->8、Redis 生产问题<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、缓存穿透" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、缓存穿透"><!---->1、缓存穿透<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、缓存雪崩" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、缓存雪崩"><!---->2、缓存雪崩<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、缓存击穿-分布式项目情况下涉及到分布式锁" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、缓存击穿（分布式项目情况下涉及到分布式锁）"><!---->3、缓存击穿（分布式项目情况下涉及到分布式锁）<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、如何保证缓存和数据库数据的一致性" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、如何保证缓存和数据库数据的一致性？"><!---->4、如何保证缓存和数据库数据的一致性？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_5、redis-怎么实现消息队列" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5、Redis 怎么实现消息队列？"><!---->5、Redis 怎么实现消息队列？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_9、3种常用的缓存读写策略详解" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="9、3种常用的缓存读写策略详解"><!---->9、3种常用的缓存读写策略详解<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#cache-aside-pattern-旁路缓存模式" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="Cache Aside Pattern （旁路缓存模式）"><!---->Cache Aside Pattern （旁路缓存模式）<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#read-write-through-pattern-读写穿透" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="Read/Write Through Pattern（读写穿透）"><!---->Read/Write Through Pattern（读写穿透）<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#write-behind-pattern-异步缓存写入" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="Write Behind Pattern（异步缓存写入）"><!---->Write Behind Pattern（异步缓存写入）<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_10、redis-内存碎片详解" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="10、Redis 内存碎片详解"><!---->10、Redis 内存碎片详解<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、什么是内存碎片" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、什么是内存碎片?"><!---->1、什么是内存碎片?<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、为什么会有-redis-内存碎片" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、为什么会有 Redis 内存碎片?"><!---->2、为什么会有 Redis 内存碎片?<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、如何查看-redis-内存碎片的信息" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、如何查看 Redis 内存碎片的信息？"><!---->3、如何查看 Redis 内存碎片的信息？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、如何清理-redis-内存碎片" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、如何清理 Redis 内存碎片？"><!---->4、如何清理 Redis 内存碎片？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_11、redis集群" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="11、Redis集群"><!---->11、Redis集群<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#常见问题" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="常见问题"><!---->常见问题<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#redis的常见部署方式" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="Redis的常见部署方式"><!---->Redis的常见部署方式<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、何时使用redis集群方案" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、何时使用Redis集群方案？"><!---->1、何时使用Redis集群方案？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、主从复制-同步" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、主从复制（同步）"><!---->2、主从复制（同步）<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、redis-sentinel-哨兵" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、Redis Sentinel（哨兵）"><!---->3、Redis Sentinel（哨兵）<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_4、redis-cluster-分片-解决写压力太大或者缓存数据量太大" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4、Redis Cluster（分片，解决写压力太大或者缓存数据量太大）"><!---->4、Redis Cluster（分片，解决写压力太大或者缓存数据量太大）<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_12、场景题" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="12、场景题"><!---->12、场景题<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_1、mysql-里有-2000w-数据-redis-中只存-20w-的数据-如何保证-redis-中的数据都是热点数据" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="1、MySQL 里有 2000w 数据，redis 中只存 20w 的数据，如何保证 redis 中的数据都是热点数据？"><!---->1、MySQL 里有 2000w 数据，redis 中只存 20w 的数据，如何保证 redis 中的数据都是热点数据？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_2、如-redis-里面有-1-亿个-key-其中有-10w-个-key-是以某个固定的已知的前缀开头的-如果将它们全部找出来" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="2、如 Redis 里面有 1 亿个 key，其中有 10w 个 key 是以某个固定的已知的前缀开头的，如果将它们全部找出来？"><!---->2、如 Redis 里面有 1 亿个 key，其中有 10w 个 key 是以某个固定的已知的前缀开头的，如果将它们全部找出来？<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/zh/interview/Redis.html#_3、一个-redis-实例最多能存放多少的-keys-list、set、sorted-set-他们最多能存放多少元素" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="3、一个 Redis 实例最多能存放多少的 keys？List、Set、Sorted Set 他们最多能存放多少元素？"><!---->3、一个 Redis 实例最多能存放多少的 keys？List、Set、Sorted Set 他们最多能存放多少元素？<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6.html" class="nav-link sidebar-link sidebar-page" aria-label="主流框架"><span class="icon iconfont icon-lock" style=""></span>主流框架<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%8E%E9%AB%98%E6%80%A7%E8%83%BD%E9%AB%98%E5%8F%AF%E7%94%A8.html" class="nav-link sidebar-link sidebar-page" aria-label="分布式"><span class="icon iconfont icon-lock" style=""></span>分布式<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.html" class="nav-link sidebar-link sidebar-page" aria-label="操作系统"><span class="icon iconfont icon-lock" style=""></span>操作系统<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E6%99%BA%E5%8A%9B%E9%A2%98.html" class="nav-link sidebar-link sidebar-page" aria-label="智力题"><span class="icon iconfont icon-lock" style=""></span>智力题<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1.html" class="nav-link sidebar-link sidebar-page" aria-label="系统设计"><span class="icon iconfont icon-lock" style=""></span>系统设计<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.html" class="nav-link sidebar-link sidebar-page" aria-label="计算机网络"><span class="icon iconfont icon-lock" style=""></span>计算机网络<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/interview/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html" class="nav-link sidebar-link sidebar-page" aria-label="设计模式"><!---->设计模式<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading clickable"><span class="icon iconfont icon-creative" style=""></span><a href="/zh/guide/" class="title">如何使用</a><!----></p><ul class="sidebar-links"><li><!--[--><a href="/zh/guide/page.html" class="nav-link sidebar-link sidebar-page" aria-label="页面配置"><span class="icon iconfont icon-page" style=""></span>页面配置<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/guide/markdown.html" class="nav-link sidebar-link sidebar-page" aria-label="Markdown 展示"><span class="icon iconfont icon-markdown" style=""></span>Markdown 展示<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/guide/disable.html" class="nav-link sidebar-link sidebar-page" aria-label="布局与功能禁用"><span class="icon iconfont icon-config" style=""></span>布局与功能禁用<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/guide/encrypt.html" class="nav-link sidebar-link sidebar-page" aria-label="密码加密的文章"><span class="icon iconfont icon-lock" style=""></span>密码加密的文章<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading"><span class="icon iconfont icon-note" style=""></span><span class="title">文章</span><!----></p><ul class="sidebar-links"><li><!--[--><section class="sidebar-group"><button class="sidebar-heading clickable"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 1-4</span><span class="arrow right"></span></button><!----></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 5-12</span><!----></p><ul class="sidebar-links"><li><!--[--><section class="sidebar-group"><button class="sidebar-heading clickable"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 5-8</span><span class="arrow right"></span></button><!----></section><!--]--></li><li><!--[--><section class="sidebar-group"><p class="sidebar-heading"><span class="icon iconfont icon-note" style=""></span><span class="title">文章 9-12</span><!----></p><ul class="sidebar-links"><li><!--[--><a href="/zh/posts/article9.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 9"><span class="icon iconfont icon-edit" style=""></span>文章 9<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/posts/article10.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 10"><span class="icon iconfont icon-edit" style=""></span>文章 10<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/posts/article11.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 11"><span class="icon iconfont icon-edit" style=""></span>文章 11<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/zh/posts/article12.html" class="nav-link sidebar-link sidebar-page" aria-label="文章 12"><span class="icon iconfont icon-edit" style=""></span>文章 12<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section><!--]--></li></ul></section><!--]--></li></ul></section><!--]--></li></ul><!--[--><!----><!--]--></aside><!--[--><main class="page" id="main-content"><!--[--><!----><nav class="breadcrumb disable"></nav><div class="page-title"><h1><span class="icon iconfont icon-lock" style=""></span>Redis</h1><div class="page-info"><span class="author-info" aria-label="作者🖊" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon author-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="author icon"><path d="M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"></path></svg><span><a class="author-item" href="https://changlu.blog.csdn.net" target="_blank" rel="noopener noreferrer">长路</a></span><span property="author" content="长路"></span></span><!----><!----><span class="category-info" aria-label="分类🌈" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon category-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="category icon"><path d="M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"></path></svg><ul class="categories-wrapper"><li class="category category8 clickable" role="navigation">面试题</li><meta property="articleSection" content="面试题"></ul></span><span aria-label="标签🏷" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon tag-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="tag icon"><path d="M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"></path></svg><ul class="tags-wrapper"><li class="tag tag3 clickable" role="navigation">Redis</li></ul><meta property="keywords" content="Redis"></span><span class="reading-time-info" aria-label="阅读时间⌛" data-balloon-pos="down" isoriginal="false" pageview="false"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="timer icon"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>大约 69 分钟</span><meta property="timeRequired" content="PT69M"></span></div><hr></div><div class="toc-place-holder"><aside id="toc"><div class="toc-header">此页内容</div><div class="toc-wrapper"><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis基础" class="router-link-active router-link-exact-active toc-link level2">1、Redis基础</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、简单介绍一下-redis" class="router-link-active router-link-exact-active toc-link level3">1、简单介绍一下 Redis!</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、分布式缓存常见的技术选型方案有哪些" class="router-link-active router-link-exact-active toc-link level3">2、分布式缓存常见的技术选型方案有哪些？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、说一下-redis-和-memcached-的区别和共同点" class="router-link-active router-link-exact-active toc-link level3">3、说一下 Redis 和 Memcached 的区别和共同点</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、缓存数据的处理流程" class="router-link-active router-link-exact-active toc-link level3">4、缓存数据的处理流程</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_5、为什么要用-redis-为什么要用缓存" class="router-link-active router-link-exact-active toc-link level3">5、为什么要用 Redis/为什么要用缓存？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_6、-redis-除了做缓存-还能做什么" class="router-link-active router-link-exact-active toc-link level3">6、 Redis 除了做缓存，还能做什么？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_7、redis-可以做消息队列么" class="router-link-active router-link-exact-active toc-link level3">7、Redis 可以做消息队列么？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_8、redis的应用场景有哪些" class="router-link-active router-link-exact-active toc-link level3">8、Redis的应用场景有哪些？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_9、redis命令" class="router-link-active router-link-exact-active toc-link level3">9、Redis命令</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_10、pipeline的作用" class="router-link-active router-link-exact-active toc-link level3">10、pipeline的作用？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_11、lua脚本-场景-限流" class="router-link-active router-link-exact-active toc-link level3">11、LUA脚本（场景：限流）</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_12、什么是redlock" class="router-link-active router-link-exact-active toc-link level3">12、什么是RedLock？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_13、jedis-与-redisson-对比有什么优缺点" class="router-link-active router-link-exact-active toc-link level3">13、Jedis 与 Redisson 对比有什么优缺点？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、redis-常见数据结构" class="router-link-active router-link-exact-active toc-link level2">2、Redis 常见数据结构</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、基本的和特殊的数据类型" class="router-link-active router-link-exact-active toc-link level3">1、基本的和特殊的数据类型</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、sortedset和list异同点" class="router-link-active router-link-exact-active toc-link level3">2、SortedSet和List异同点？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、redis-线程模型" class="router-link-active router-link-exact-active toc-link level2">3、Redis 线程模型</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis-单线程模型了解" class="router-link-active router-link-exact-active toc-link level3">1、Redis 单线程模型了解</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、redis6-0-之前为什么不使用多线程" class="router-link-active router-link-exact-active toc-link level3">2、Redis6.0 之前为什么不使用多线程？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、redis6-0-之后为何引入了多线程" class="router-link-active router-link-exact-active toc-link level3">3、Redis6.0 之后为何引入了多线程？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、redis-内存管理" class="router-link-active router-link-exact-active toc-link level2">4、Redis 内存管理</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis-给缓存数据设置过期时间有啥用" class="router-link-active router-link-exact-active toc-link level3">1、Redis 给缓存数据设置过期时间有啥用？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、-redis-是如何判断数据是否过期的呢" class="router-link-active router-link-exact-active toc-link level3">2、 Redis 是如何判断数据是否过期的呢？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、过期的数据的删除策略了解么" class="router-link-active router-link-exact-active toc-link level3">3、过期的数据的删除策略了解么？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、redis-内存淘汰机制了解么" class="router-link-active router-link-exact-active toc-link level3">4、Redis 内存淘汰机制了解么？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_5、redis-持久化机制" class="router-link-active router-link-exact-active toc-link level2">5、Redis 持久化机制</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、怎么保证-redis-挂掉之后再重启数据可以进行恢复" class="router-link-active router-link-exact-active toc-link level3">1、怎么保证 Redis 挂掉之后再重启数据可以进行恢复？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、什么是-rdb-持久化" class="router-link-active router-link-exact-active toc-link level3">2、什么是 RDB 持久化？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、什么是-aof-持久化" class="router-link-active router-link-exact-active toc-link level3">3、什么是 AOF 持久化？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、rdb与aof如何选择" class="router-link-active router-link-exact-active toc-link level3">4、RDB与AOF如何选择？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_5、aof-重写了解吗" class="router-link-active router-link-exact-active toc-link level3">5、AOF 重写了解吗？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_6、redis-4-0-对于持久化机制做了什么优化" class="router-link-active router-link-exact-active toc-link level3">6、Redis 4.0 对于持久化机制做了什么优化？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_6、redis事务" class="router-link-active router-link-exact-active toc-link level2">6、Redis事务</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、如何使用-redis-事务" class="router-link-active router-link-exact-active toc-link level3">1、如何使用 Redis 事务？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、redis-支持原子性吗" class="router-link-active router-link-exact-active toc-link level3">2、Redis 支持原子性吗？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、如何解决-redis-事务的缺陷" class="router-link-active router-link-exact-active toc-link level3">3、如何解决 Redis 事务的缺陷？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_7、redis-性能优化" class="router-link-active router-link-exact-active toc-link level2">7、Redis 性能优化</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、redis-bigkey" class="router-link-active router-link-exact-active toc-link level3">1、Redis bigkey</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、大量-key-集中过期问题" class="router-link-active router-link-exact-active toc-link level3">2、大量 key 集中过期问题</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_8、redis-生产问题" class="router-link-active router-link-exact-active toc-link level2">8、Redis 生产问题</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、缓存穿透" class="router-link-active router-link-exact-active toc-link level3">1、缓存穿透</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、缓存雪崩" class="router-link-active router-link-exact-active toc-link level3">2、缓存雪崩</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、缓存击穿-分布式项目情况下涉及到分布式锁" class="router-link-active router-link-exact-active toc-link level3">3、缓存击穿（分布式项目情况下涉及到分布式锁）</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、如何保证缓存和数据库数据的一致性" class="router-link-active router-link-exact-active toc-link level3">4、如何保证缓存和数据库数据的一致性？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_5、redis-怎么实现消息队列" class="router-link-active router-link-exact-active toc-link level3">5、Redis 怎么实现消息队列？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_9、3种常用的缓存读写策略详解" class="router-link-active router-link-exact-active toc-link level2">9、3种常用的缓存读写策略详解</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#cache-aside-pattern-旁路缓存模式" class="router-link-active router-link-exact-active toc-link level3">Cache Aside Pattern （旁路缓存模式）</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#read-write-through-pattern-读写穿透" class="router-link-active router-link-exact-active toc-link level3">Read/Write Through Pattern（读写穿透）</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#write-behind-pattern-异步缓存写入" class="router-link-active router-link-exact-active toc-link level3">Write Behind Pattern（异步缓存写入）</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_10、redis-内存碎片详解" class="router-link-active router-link-exact-active toc-link level2">10、Redis 内存碎片详解</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、什么是内存碎片" class="router-link-active router-link-exact-active toc-link level3">1、什么是内存碎片?</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、为什么会有-redis-内存碎片" class="router-link-active router-link-exact-active toc-link level3">2、为什么会有 Redis 内存碎片?</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、如何查看-redis-内存碎片的信息" class="router-link-active router-link-exact-active toc-link level3">3、如何查看 Redis 内存碎片的信息？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、如何清理-redis-内存碎片" class="router-link-active router-link-exact-active toc-link level3">4、如何清理 Redis 内存碎片？</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_11、redis集群" class="router-link-active router-link-exact-active toc-link level2">11、Redis集群</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#常见问题" class="router-link-active router-link-exact-active toc-link level3">常见问题</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#redis的常见部署方式" class="router-link-active router-link-exact-active toc-link level3">Redis的常见部署方式</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、何时使用redis集群方案" class="router-link-active router-link-exact-active toc-link level3">1、何时使用Redis集群方案？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、主从复制-同步" class="router-link-active router-link-exact-active toc-link level3">2、主从复制（同步）</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、redis-sentinel-哨兵" class="router-link-active router-link-exact-active toc-link level3">3、Redis Sentinel（哨兵）</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_4、redis-cluster-分片-解决写压力太大或者缓存数据量太大" class="router-link-active router-link-exact-active toc-link level3">4、Redis Cluster（分片，解决写压力太大或者缓存数据量太大）</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_12、场景题" class="router-link-active router-link-exact-active toc-link level2">12、场景题</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_1、mysql-里有-2000w-数据-redis-中只存-20w-的数据-如何保证-redis-中的数据都是热点数据" class="router-link-active router-link-exact-active toc-link level3">1、MySQL 里有 2000w 数据，redis 中只存 20w 的数据，如何保证 redis 中的数据都是热点数据？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_2、如-redis-里面有-1-亿个-key-其中有-10w-个-key-是以某个固定的已知的前缀开头的-如果将它们全部找出来" class="router-link-active router-link-exact-active toc-link level3">2、如 Redis 里面有 1 亿个 key，其中有 10w 个 key 是以某个固定的已知的前缀开头的，如果将它们全部找出来？</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/zh/interview/Redis.html#_3、一个-redis-实例最多能存放多少的-keys-list、set、sorted-set-他们最多能存放多少元素" class="router-link-active router-link-exact-active toc-link level3">3、一个 Redis 实例最多能存放多少的 keys？List、Set、Sorted Set 他们最多能存放多少元素？</a></li><!----><!--]--></ul><!--]--></ul></div></aside></div><!----><div class="theme-hope-content"><h1 id="redis" tabindex="-1"><a class="header-anchor" href="#redis" aria-hidden="true">#</a> Redis</h1><h2 id="_1、redis基础" tabindex="-1"><a class="header-anchor" href="#_1、redis基础" aria-hidden="true">#</a> 1、Redis基础</h2><h3 id="_1、简单介绍一下-redis" tabindex="-1"><a class="header-anchor" href="#_1、简单介绍一下-redis" aria-hidden="true">#</a> 1、简单介绍一下 Redis!</h3><p><strong>简介</strong>： Redis 就是一个使用 C 语言开发的数据库。<strong>Redis 的数据是存在内存中的</strong> ，也就是它是内存数据库，所以读写速度非常快，因此 Redis 被广泛应用于缓存方向。</p><p><strong>用途</strong>：除了做缓存之外，也经常用来做分布式锁，甚至是消息队列。</p><p><strong>数据类型作用</strong>：Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。</p><h3 id="_2、分布式缓存常见的技术选型方案有哪些" tabindex="-1"><a class="header-anchor" href="#_2、分布式缓存常见的技术选型方案有哪些" aria-hidden="true">#</a> 2、分布式缓存常见的技术选型方案有哪些？</h3><p>分布式缓存的话，使用的比较多的主要是 <strong>Memcached</strong> 和 <strong>Redis</strong>。</p><p>Memcached 是分布式缓存最开始兴起的那会，比较常用的。后来，随着 Redis 的发展，大家慢慢都转而使用更加强大的 Redis 了。</p><p>分布式缓存主要解决的是<strong>单机缓存的容量受服务器限制并且无法保存通用信息的问题</strong>。因为，本地缓存只在当前服务里有效，比如如果你部署了两个相同的服务，他们两者之间的缓存数据是无法共同的。</p><h3 id="_3、说一下-redis-和-memcached-的区别和共同点" tabindex="-1"><a class="header-anchor" href="#_3、说一下-redis-和-memcached-的区别和共同点" aria-hidden="true">#</a> 3、说一下 Redis 和 Memcached 的区别和共同点</h3><p>现在公司一般都是用 Redis 来实现缓存，而且 Redis 自身也越来越强大了！不过，了解 Redis 和 Memcached 的区别和共同点，有助于我们在做相应的技术选型的时候，能够做到有理有据！</p><p>共同点：①作为缓存。②都有过期策略。③两者性能都比较高。</p><p>区别：</p><ul><li>Redis支持更丰富的数据类型；</li><li>能够进行持久化；</li><li>若是服务器内存不够，可以将不用的数据缓存到磁盘上；</li><li>支持cluster集群模式；</li><li>Redis是单线程的，6.0之后则是多线程。</li><li>Redis支持发布订阅模型、Lua 脚本、事务等功能，Redis支持更多的编程语言。</li><li>Redis包含惰性删除与定期删除；Memcached只有惰性删除。</li></ul><h3 id="_4、缓存数据的处理流程" tabindex="-1"><a class="header-anchor" href="#_4、缓存数据的处理流程" aria-hidden="true">#</a> 4、缓存数据的处理流程</h3><p>简单来说就是:</p><ol><li>如果用户请求的数据在缓存中就直接返回。</li><li>缓存中不存在的话就看数据库中是否存在。</li><li>数据库中存在的话就更新缓存中的数据。</li><li>数据库中不存在的话就返回空数据。</li></ol><h3 id="_5、为什么要用-redis-为什么要用缓存" tabindex="-1"><a class="header-anchor" href="#_5、为什么要用-redis-为什么要用缓存" aria-hidden="true">#</a> 5、为什么要用 Redis/为什么要用缓存？</h3><p>从“高性能”和“高并发”这两点来看待这个问题。</p><p><strong>高性能</strong> ：假如用户第一次访问数据库中的某些数据的话，这个过程是比较慢，毕竟是从硬盘中读取的。但是，如果说，用户访问的数据属于高频数据并且不会经常改变的话，那么我们就可以很放心地将该用户访问的数据存在缓存中。</p><ul><li>不过，要保持数据库和缓存中的数据的一致性。 如果数据库中的对应数据改变的之后，同步改变缓存中相应的数据即可！</li></ul><p>**高并发：**一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右（4 核 8g） ，但是使用 Redis 缓存之后很容易达到 10w+，甚至最高能达到 30w+（就单机 redis 的情况，redis 集群的话会更高）。</p><p>由此可见，直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的，所以我们可以考虑把数据库中的部分数据转移到缓存中去，这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而，我们也就提高了系统整体的并发。</p><h3 id="_6、-redis-除了做缓存-还能做什么" tabindex="-1"><a class="header-anchor" href="#_6、-redis-除了做缓存-还能做什么" aria-hidden="true">#</a> 6、 Redis 除了做缓存，还能做什么？</h3><ul><li><strong>分布式锁</strong> ： 通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下，我们都是基于 Redisson 来实现分布式锁。相关阅读：<a href="https://mp.weixin.qq.com/s/CbnPRfvq4m1sqo2uKI6qQw" target="_blank" rel="noopener noreferrer">《分布式锁中的王者方案 - Redisson》open in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>。</li><li><strong>限流</strong> ：一般是通过 Redis + Lua 脚本的方式来实现限流。相关阅读：<a href="https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA" target="_blank" rel="noopener noreferrer">《我司用了 6 年的 Redis 分布式限流器，可以说是非常厉害了！》open in new window<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>。</li><li><strong>消息队列</strong> ：Redis 自带的 list 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka，有主题和消费组的概念，支持消息持久化以及 ACK 机制。</li><li><strong>复杂业务场景</strong> ：通过 Redis 以及 Redis 扩展（比如 Redisson）提供的数据结构，我们可以很方便地完成很多复杂的业务场景比如通过 bitmap 统计活跃用户、通过 sorted set 维护排行榜。</li></ul><h3 id="_7、redis-可以做消息队列么" tabindex="-1"><a class="header-anchor" href="#_7、redis-可以做消息队列么" aria-hidden="true">#</a> 7、Redis 可以做消息队列么？</h3><p>Redis 5.0 新增加的一个数据结构 <code>Stream</code> 可以用来做消息队列，<code>Stream</code> 支持：</p><ul><li>发布 / 订阅模式</li><li>按照消费者组进行消费</li><li>消息持久化（ RDB 和 AOF）</li></ul><p>不过，和专业的消息队列相比，还是有很多欠缺的地方比如<strong>消息丢失和堆积问题不好解决</strong>。因此，我们通常<strong>建议是不使用 Redis 来做消息队列的</strong>，你<strong>完全可以选择市面上比较成熟的一些消息队列</strong>比如 RocketMQ、Kafka。</p><h3 id="_8、redis的应用场景有哪些" tabindex="-1"><a class="header-anchor" href="#_8、redis的应用场景有哪些" aria-hidden="true">#</a> 8、Redis的应用场景有哪些？</h3><ol><li><p><strong>缓存热点数据</strong>，缓解数据库的压力。</p></li><li><p>利用 Redis 原子性的自增操作，可以<strong>实现计数器的功能，比如统计用户点赞数、用户访问数等</strong>。</p></li><li><p>简单的消息队列，可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列，<strong>实现异步操作</strong>。</p></li><li><p><strong>限速器</strong>，可用于限制某个用户访问某个接口的频率，比如秒杀场景用于防止用户快速点击带来不必 要的压力。</p></li><li><p><strong>好友关系</strong>，利用集合的一些命令，比如交集、并集、差集等，实现共同好友、共同爱好之类的功 能。</p></li></ol><h3 id="_9、redis命令" tabindex="-1"><a class="header-anchor" href="#_9、redis命令" aria-hidden="true">#</a> 9、Redis命令</h3><h4 id="_1、keys命令存在的问题" tabindex="-1"><a class="header-anchor" href="#_1、keys命令存在的问题" aria-hidden="true">#</a> 1、keys命令存在的问题？</h4><blockquote><p><a href="https://blog.csdn.net/lq2016510/article/details/123046747" target="_blank" rel="noopener noreferrer">记录~Redis命令scan的使用<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p></blockquote><p><strong>问题描述</strong>：数据量达到几百万，keys这个指令就会导致 Redis 服务卡顿，因为 Redis 是单线程程序，顺序执行所有指令，其它指令必须等到当前的 keys 指令执行完了才可以继续 。</p><p><strong>解决方案</strong>：scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题，每次scan命令的时间复杂度是 O(1) ，但是要真正实现keys的功能，需要执行多次scan。</p><p><strong>使用方式</strong>：<code>scan 游标 match &lt;给定模式相匹配的元素&gt; count 每次迭代所返回的元素数量</code></p><p><strong>效果</strong>：</p><ul><li>SCAN命令是增量的循环，每次调用只会返回一小部分的元素。所以不会让redis假死。</li><li>SCAN命令返回的是一个游标，从0开始遍历，到0结束遍历。</li></ul><p><strong>scan的缺点</strong>：在scan的过程中如果有键的变化（增加、删除、修改），遍历过程可能会有以下问题：新增的键可能没有遍历到，遍历出了重复的键等情况，也就是说scan并不能保证完整的遍历出来所有的键。</p><h3 id="_10、pipeline的作用" tabindex="-1"><a class="header-anchor" href="#_10、pipeline的作用" aria-hidden="true">#</a> 10、pipeline的作用？</h3><blockquote><p><a href="https://blog.csdn.net/mumuwang1234/article/details/118603697" target="_blank" rel="noopener noreferrer">redis中的pipeline（管道）<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p></blockquote><p>edis客户端执行一条命令分4个过程： 发送命令、命令排队、命令执行、返回结果。使用 pipeline 可以批量请求，批量返回结果，执行速度比逐条执行要快。使用 pipeline 组装的命令个数不能太多，不然数据量过大，增加客户端的等待时间，还可能造成网络阻塞，可以将大量命令的拆分多个小的 pipeline 命令完成。</p><p>原生批命令（mset和mget）与 pipeline 对比：</p><ol><li>原生批命令是原子性， pipeline 是非原子性。pipeline命令中途异常退出，之前执行成功的命令 不会回滚。</li><li>原生批命令只有一个命令，但 pipeline 支持多命令。</li></ol><h3 id="_11、lua脚本-场景-限流" tabindex="-1"><a class="header-anchor" href="#_11、lua脚本-场景-限流" aria-hidden="true">#</a> 11、LUA脚本（场景：限流）</h3><p>Redis 通过 LUA 脚本创建具有原子性的命令： 当lua脚本命令正在运行的时候，不会有其他脚本或Redis 命令被执行，实现组合命令的原子操作。</p><p>在Redis中执行Lua脚本有两种方法： eval 和 evalsha 。 eval 命令使用内置的 Lua 解释器，对 Lua 脚本进行求值。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code>//第一个参数是lua脚本，第二个参数是键名参数个数，剩下的是键名参数和附加参数<span class="token operator">&gt;</span> <span class="token builtin class-name">eval</span> <span class="token string">&quot;return
{KEYS[1],KEYS[2],ARGV[1],ARGV[2]}&quot;</span> <span class="token number">2</span> key1 key2 first second1<span class="token punctuation">)</span> <span class="token string">&quot;key1&quot;</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token string">&quot;key2&quot;</span><span class="token number">3</span><span class="token punctuation">)</span>
<span class="token string">&quot;first&quot;</span><span class="token number">4</span><span class="token punctuation">)</span> <span class="token string">&quot;second&quot;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>lua脚本作用</strong>：</p><p>1、Lua脚本在Redis中是原子执行的，执行过程中间不会插入其他命令。</p><p>2、Lua脚本可以将多条命令一次性打包，有效地减少网络开销。</p><p><strong>应用场景</strong>：</p><p><strong>举例</strong>：限制接口访问频率。</p><p>在Redis维护一个接口访问次数的键值对， key 是接口名称， value 是访问次数。每次访问接口时，会执行以下操作：</p><ul><li>通过 aop 拦截接口的请求，对接口请求进行计数，每次进来一个请求，相应的接口访问次数 count加1，存入redis。</li><li>如果是第一次请求，则会设置 count=1 ，并设置过期时间。因为这里 set() 和 expire() 组合操作不是原子操作，所以引入 lua 脚本，实现原子操作，避免并发访问问题。</li><li>如果给定时间范围内超过最大访问次数，则会抛出异常</li></ul><div class="language-java ext-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">private</span> <span class="token class-name">String</span> <span class="token function">buildLuaScript</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token string">&quot;local c&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nc = redis.call(&#39;get&#39;,KEYS[1])&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nif c and tonumber(c) &gt; tonumber(ARGV[1]) then&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nreturn c;&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nend&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nc = redis.call(&#39;incr&#39;,KEYS[1])&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nif tonumber(c) == 1 then&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nredis.call(&#39;expire&#39;,KEYS[1],ARGV[2])&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nend&quot;</span> <span class="token operator">+</span>
<span class="token string">&quot;\nreturn c;&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token class-name">String</span> luaScript <span class="token operator">=</span> <span class="token function">buildLuaScript</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">RedisScript</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Number</span><span class="token punctuation">&gt;</span></span> redisScript <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultRedisScript</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>luaScript<span class="token punctuation">,</span>
<span class="token class-name">Number</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Number</span> count <span class="token operator">=</span> redisTemplate<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span>redisScript<span class="token punctuation">,</span> keys<span class="token punctuation">,</span> limit<span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
limit<span class="token punctuation">.</span><span class="token function">period</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>PS：这种接口限流的实现方式比较简单，问题也比较多，一般不会使用，接口限流用的比较多的是令牌桶算法和漏桶算法。</p><h3 id="_12、什么是redlock" tabindex="-1"><a class="header-anchor" href="#_12、什么是redlock" aria-hidden="true">#</a> 12、什么是RedLock？</h3><p>Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock，此种方式比原先的单节点的方法更安全。它可以保证以下特性：</p><ol><li>安全特性：互斥访问，即永远只有一个 client 能拿到锁。</li><li>避免死锁：最终 client 都可能拿到锁，不会出现死锁的情况，即使原本锁住某资源的 client 挂掉了。</li><li>容错性：只要大部分 Redis 节点存活就可以正常提供服务。</li></ol><h3 id="_13、jedis-与-redisson-对比有什么优缺点" tabindex="-1"><a class="header-anchor" href="#_13、jedis-与-redisson-对比有什么优缺点" aria-hidden="true">#</a> 13、Jedis 与 Redisson 对比有什么优缺点？</h3><p>Jedis 是 Redis 的 Java 实现的客户端，其 API 提供了比较全面的 Redis 命令的支持；Redisson 实现了分布式和可扩展的 Java 数据结构，和 Jedis 相比，功能较为简单，不支持字符串操作，不支持排序、事务、管道、分区等 Redis 特性。</p><p>Redisson 的宗旨是促进使用者对 Redis 的关注分离，从而让使用者能够将精力更集中地放在处理业务逻辑上。</p><h2 id="_2、redis-常见数据结构" tabindex="-1"><a class="header-anchor" href="#_2、redis-常见数据结构" aria-hidden="true">#</a> 2、Redis 常见数据结构</h2><h3 id="_1、基本的和特殊的数据类型" tabindex="-1"><a class="header-anchor" href="#_1、基本的和特殊的数据类型" aria-hidden="true">#</a> 1、基本的和特殊的数据类型</h3><blockquote><p><strong>基本数据类型</strong></p></blockquote><p>1、<code> string</code>：简单的 key-value 类型，最常用的一种数据类型，String类型的值可以是字符串、数字或者二进制，但值最大不能超 过512MB。</p><ul><li><strong>常用命令：</strong> <code>set,get,strlen,exists,decr,incr,setex</code> 等等。</li><li><strong>应用场景：</strong> 一般常用在需要计数的场景，比如用户的访问次数、热点文章的点赞转发数量等等。</li></ul><p>2、<code> list</code>：有序可重复的集合。list 的实现为一个 <strong>双向链表</strong>，即可以支持反向查找和遍历，更方便操作，不过带来了部分额外的内存开销。</p><ul><li><strong>常用命令:</strong> <code>rpush,lpop,lpush,rpop,lrange,llen</code> 等。</li><li><strong>应用场景:</strong> 发布与订阅或者说消息队列、慢查询。</li></ul><p>3、<code>hash</code>：Hash 是一个键值对集合。内部数组 + 链表，做了更多的优化，<strong>特别适合用于存储对象</strong>，后续操作的时候，你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息，商品信息等等。</p><ul><li><strong>常用命令：</strong> <code>hset,hmset,hexists,hget,hgetall,hkeys,hvals</code> 等。</li><li><strong>应用场景:</strong> 系统中对象数据的存储。</li></ul><p>4、<code>set</code>：set 类似于 Java 中的 <code>HashSet</code>。比如：你可以将一个用户所有的关注人存在一个集合中，将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。</p><ul><li><strong>常用命令：</strong> <code>sadd,spop,smembers,sismember,scard,sinterstore,sunion</code> 等。</li><li><strong>应用场景:</strong> 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景</li></ul><p>5、<code> sorted set</code>：有序Set。内部维护了一个 score 的参数来实现。适用于排行榜和带权重的消息队列等场景。</p><ul><li><strong>常用命令：</strong> <code>zadd,zcard,zscore,zrange,zrevrange,zrem</code> 等。</li><li><strong>应用场景：</strong> 需要对数据根据某个权重进行排序的场景。比如在直播系统中，实时排行信息包含<strong>直播间在线用户列表，各种礼物排行榜，弹幕消息</strong>（可以理解为按消息维度的消息排行榜）等信息。</li></ul><blockquote><p><strong>特殊的数据类型</strong></p></blockquote><p>1、<code> bitmap</code>：位图，bitmap 存储的是连续的二进制数字（0 和 1），通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态，key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte，所以 bitmap 本身会极大的节省储存空间。</p><ul><li><strong>常用命令：</strong> <code>setbit</code> 、<code>getbit</code> 、<code>bitcount</code>、<code>bitop</code></li><li><strong>应用场景：</strong> 适合需要保存状态信息（比如是否签到、是否登录...）并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计（比如是否点赞过某个视频）。</li></ul><p><strong>使用场景一：用户行为分析</strong> 很多网站为了分析你的喜好，需要研究你点赞过的内容。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token comment"># 记录你喜欢过 001 号小姐姐</span>
<span class="token number">127.0</span>.0.1:637<span class="token operator"><span class="token file-descriptor important">9</span>&gt;</span> setbit beauty_girl_001 uid <span class="token number">1</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>使用场景二：统计活跃用户</strong></p><p>使用时间作为 key，然后用户 ID 为 offset，如果当日活跃过就设置为 1</p><p><strong>使用场景三：用户在线状态</strong></p><p>对于获取或者统计用户在线状态，使用 bitmap 是一个节约空间且效率又高的一种方法。</p><p>只需要一个 key，然后用户 ID 为 offset，如果在线就设置为 1，不在线就设置为 0。</p><p>2、<code>Hyperloglog</code>：HyperLogLog 是用来做<strong>基数统计的算法</strong>，其优点是，在输入元素的数量或者体积非常非常大时，计算基数所需的空间总是固定的、并且是很小的。典型的使用场景是统计独立访客。</p><p>3、<code>Geospatial</code> ：主要用于<strong>存储地理位置信息</strong>，并对存储的信息进行操作，适用场景如定位、附近的人等。</p><h3 id="_2、sortedset和list异同点" tabindex="-1"><a class="header-anchor" href="#_2、sortedset和list异同点" aria-hidden="true">#</a> 2、SortedSet和List异同点？</h3><p><strong>相同点</strong>：</p><p>1、都是有序的；</p><p>2、都可以获得某个范围内的元素。</p><p><strong>不同点</strong>：</p><p>1、列表基于链表实现，获取两端元素速度快，访问中间元素速度慢；</p><p>2、有序集合基于<strong>散列表和跳跃表</strong>实现，访问中间元素时间复杂度是<code>OlogN</code>；</p><p>3、列表不能简单的调整某个元素的位置，有序列表可以（更改元素的分数）；</p><p>4、<strong>有序集合更耗内存</strong>。</p><h2 id="_3、redis-线程模型" tabindex="-1"><a class="header-anchor" href="#_3、redis-线程模型" aria-hidden="true">#</a> 3、Redis 线程模型</h2><h3 id="_1、redis-单线程模型了解" tabindex="-1"><a class="header-anchor" href="#_1、redis-单线程模型了解" aria-hidden="true">#</a> 1、Redis 单线程模型了解</h3><p>基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型</p><p>这套事件处理模型对应的是 Redis 中的文件事件处理器（file event handler），由于文件事件处理器（file event handler）是单线程方式运行的，所以我们一般都说 Redis 是单线程模型。</p><p>**既然是单线程，那怎么监听大量的客户端连接呢？**IO多路复用。</p><ul><li><strong>I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接，降低了资源的消耗</strong>（和 NIO 中的 <code>Selector</code> 组件很像）。</li></ul><p>Redis 服务器是一个事件驱动程序，服务器需要处理两类事件：1. 文件事件; 2. 时间事件。</p><ul><li>当被监听的套接字准备好执行连接应答（accept）、读取（read）、写入（write）、关 闭（close）等操作时，与操作相对应的文件事件就会产生，这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。</li><li><strong>虽然文件事件处理器以单线程方式运行，但通过使用 I/O 多路复用程序来监听多个套接字</strong>，文件事件处理器既实现了高性能的网络通信模型，又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接，这保持了 Redis 内部单线程设计的简单性。</li></ul><p>文件事件处理器（file event handler）主要是包含 4 个部分：</p><ul><li>多个 socket（客户端连接）</li><li>IO 多路复用程序（支持多个客户端连接的关键）</li><li>文件事件分派器（将 socket 关联到相应的事件处理器）</li><li>事件处理器（连接应答处理器、命令请求处理器、命令回复处理器）</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206162340322.png" alt="image-20220616234004247" loading="lazy"></p><h3 id="_2、redis6-0-之前为什么不使用多线程" tabindex="-1"><a class="header-anchor" href="#_2、redis6-0-之前为什么不使用多线程" aria-hidden="true">#</a> 2、Redis6.0 之前为什么不使用多线程？</h3><p><strong>那，Redis6.0 之前为什么不使用多线程？</strong></p><p>我觉得主要原因有下面 3 个：</p><ol><li>单线程编程容易并且更容易维护；</li><li>Redis 的性能瓶颈不在 CPU ，主要在内存和网络；</li><li>多线程就会存在死锁、线程上下文切换等问题，甚至会影响性能。</li></ol><h3 id="_3、redis6-0-之后为何引入了多线程" tabindex="-1"><a class="header-anchor" href="#_3、redis6-0-之后为何引入了多线程" aria-hidden="true">#</a> 3、Redis6.0 之后为何引入了多线程？</h3><p><strong>Redis6.0 引入多线程主要是为了提高网络 IO 读写性能</strong>，因为这个算是 Redis 中的一个性能瓶颈（Redis 的瓶颈主要受限于内存和网络）。</p><p>虽然，Redis6.0 引入了多线程，但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了，执行命令仍然是单线程顺序执行。因此，你也不需要担心线程安全问题。</p><h2 id="_4、redis-内存管理" tabindex="-1"><a class="header-anchor" href="#_4、redis-内存管理" aria-hidden="true">#</a> 4、Redis 内存管理</h2><h3 id="_1、redis-给缓存数据设置过期时间有啥用" tabindex="-1"><a class="header-anchor" href="#_1、redis-给缓存数据设置过期时间有啥用" aria-hidden="true">#</a> 1、Redis 给缓存数据设置过期时间有啥用？</h3><p>因为内存是有限的，如果缓存中的所有数据都是一直保存的话，分分钟直接 Out of memory。</p><p>Redis 自带了给缓存数据设置过期时间的功能，比如：</p><p><strong>注意</strong>：Redis 中除了字符串类型有自己独有设置过期时间的命令 <code>setex</code> 外，其他方法都需要依靠 <code>expire</code> 命令来设置过期时间 。另外， <code>persist</code> 命令可以移除一个键的过期时间。</p><p><strong>过期时间除了有助于缓解内存的消耗，还有什么其他用么？</strong></p><ul><li>业务场景就是需要某个数据只在某一时间段内存在，比如我们的短信验证码可能只在 1 分钟内有效，用户登录的 token 可能只在 1 天内有效。</li><li>如果使用传统的数据库来处理的话，一般都是自己判断过期，这样更麻烦并且性能要差很多。</li></ul><h3 id="_2、-redis-是如何判断数据是否过期的呢" tabindex="-1"><a class="header-anchor" href="#_2、-redis-是如何判断数据是否过期的呢" aria-hidden="true">#</a> 2、 Redis 是如何判断数据是否过期的呢？</h3><p>Redis 通过一个叫做**过期字典（**可以看作是 hash 表）来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键)，过期字典的值是一个 long long 类型的整数，这个整数保存了 key 所指向的数据库键的过期时间（毫秒精度的 UNIX 时间戳）。</p><h3 id="_3、过期的数据的删除策略了解么" tabindex="-1"><a class="header-anchor" href="#_3、过期的数据的删除策略了解么" aria-hidden="true">#</a> <strong>3、过期的数据的删除策略了解么？</strong></h3><p>如果假设你设置了一批 key 只能存活 1 分钟，那么 1 分钟后，Redis 是怎么对这批 key 进行删除的呢？</p><p>常用的过期数据的删除策略就两个（重要！自己造缓存轮子的时候需要格外考虑的东西）：</p><ol><li><strong>惰性删除</strong> ：只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好，但是可能会造成太多过期 key 没有被删除。</li><li><strong>定期删除</strong> ： 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且，Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。</li></ol><p>定期删除对内存更加友好，惰性删除对 CPU 更加友好。两者各有千秋，所以 Redis 采用的是 <strong>定期删除+惰性/懒汉式删除</strong> 。</p><p>但是，仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里，然后就 Out of memory 了。</p><p>怎么解决这个问题呢？答案就是：<strong>Redis 内存淘汰机制。</strong></p><blockquote><p>牛客：<a href="https://www.nowcoder.com/exam/interview/detail?questionClassifyId=0&amp;questionId=2413054&amp;questionJobId=160&amp;type=1" target="_blank" rel="noopener noreferrer">说说Redis的缓存淘汰策略<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p></blockquote><p><strong>定时删除</strong>：在设置键的过期时间的同时，创建一个定时器 timer). 让定时器在键的过期时间来临时，立即执行对键的删除操作。</p><p><strong>惰性删除</strong>：客户端访问一个key的时候，Redis会先检查它的过期时间，如果发现过期就立刻删除这个key。</p><p><strong>定期删除</strong>：每隔一段时间程序就对数据库进行一次检查，删除里面的过期键。至于要删除多少过期键，以及要检查多少个数据库，则由算法决定。</p><p>redis会将设置了过期时间的key放到一个独立的字典中，并对该字典进行每秒10次的过期扫描，过期扫描不会遍历字典中所有的key，而是采用了一种简单的贪心策略，</p><p>该策略如下：</p><p>​ 1、从过期字典红随机选择20个key,</p><p>​ 2、删除这20个key中已过期的key，</p><p>​ 3、如果已过期key的比例超过25%，则重复步骤1；</p><p>当写入数据将超出maxmemory限制时，Redis会采用maxmemory-policy所制定的策略进行数据淘汰 即 LRU （最近最少使用原则）LRU算法的不足之处在于,若一个key很少被访问,只是刚刚偶尔被访问了一次,则它就被认为是热点数据,短时间内不会被淘汰。</p><p>LFU算法正式用于解决上述问题,LFU（Least Frequently Used）是Redis4新增的淘汰策略,它<strong>根据key的最近访问频率进行淘汰</strong>。LFU在LRU的基础上,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用LFU策略淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出内存。如果两个数据的访问次数相同,LFU再比较这两个数据的访问时间,把访问时间更早的数据淘汰出内存。</p><h3 id="_4、redis-内存淘汰机制了解么" tabindex="-1"><a class="header-anchor" href="#_4、redis-内存淘汰机制了解么" aria-hidden="true">#</a> 4、Redis 内存淘汰机制了解么？</h3><p>Redis 提供 6 种数据淘汰策略：</p><ol><li><strong>volatile-lru（least recently used）</strong>：从已设置过期时间的数据集（server.db[i].expires）中挑选最近最少使用的数据淘汰</li><li><strong>volatile-ttl</strong>：从已设置过期时间的数据集（server.db[i].expires）中挑选将要过期的数据淘汰</li><li><strong>volatile-random</strong>：从已设置过期时间的数据集（server.db[i].expires）中任意选择数据淘汰</li><li><strong>allkeys-lru（least recently used）</strong>：当内存不足以容纳新写入数据时，在键空间中，移除最近最少使用的 key（这个是最常用的）</li><li><strong>allkeys-random</strong>：从数据集（server.db[i].dict）中任意选择数据淘汰</li><li><strong>no-eviction</strong>：禁止驱逐数据，也就是说当内存不足以容纳新写入数据时，新写入操作会报错。这个应该没人使用吧！</li></ol><p>4.0 版本后增加以下两种：</p><ol><li><strong>volatile-lfu（least frequently used）</strong>：从已设置过期时间的数据集（server.db[i].expires）中挑选最不经常使用的数据淘汰</li><li><strong>allkeys-lfu（least frequently used）</strong>：当内存不足以容纳新写入数据时，在键空间中，移除最不经常使用的 key</li></ol><h2 id="_5、redis-持久化机制" tabindex="-1"><a class="header-anchor" href="#_5、redis-持久化机制" aria-hidden="true">#</a> 5、Redis 持久化机制</h2><h3 id="_1、怎么保证-redis-挂掉之后再重启数据可以进行恢复" tabindex="-1"><a class="header-anchor" href="#_1、怎么保证-redis-挂掉之后再重启数据可以进行恢复" aria-hidden="true">#</a> 1、怎么保证 Redis 挂掉之后再重启数据可以进行恢复？</h3><p>需要持久化数据也就是将内存中的数据写入到硬盘里面，大部分原因是为了之后重用数据（比如重启机器、机器故障之后恢复数据），或者是为了防止系统故障而将数据备份到一个远程位置。</p><p>Redis 不同于 Memcached 的很重要一点就是，Redis 支持持久化，而且支持两种不同的持久化操作。<strong>Redis 的一种持久化方式叫快照（snapshotting，RDB），另一种方式是只追加文件（append-only file, AOF）</strong>。</p><h3 id="_2、什么是-rdb-持久化" tabindex="-1"><a class="header-anchor" href="#_2、什么是-rdb-持久化" aria-hidden="true">#</a> 2、什么是 RDB 持久化？</h3><p>Redis 可以通过<strong>创建快照</strong>来获得存储在内存里面的数据在某个时间点上的副本。Redis 创建快照之后，可以对快照进行备份，可以将快照复制到其他服务器从而创建具有相同数据的服务器副本（Redis 主从结构，主要用来<strong>提高 Redis 性能</strong>），还可以将快照留在原地以便重启服务器的时候使用。</p><p><strong>过程</strong>：在指定目录下生成一个 dump.rdb 文件。Redis 重启会加载 <code>dump.rdb </code>文件恢复数据。</p><p><code>bgsave</code> 是主流的触发 RDB 持久化的方式，执行过程如下：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206251924377.png" alt="image-20220625192424286" loading="lazy"></p><ul><li>执行 BGSAVE 命令</li><li>Redis 父进程<strong>判断当前是否存在正在执行的子进程</strong>，如果存在， BGSAVE 命令直接返回。</li><li>父进程<strong>执行 fork 操作创建子进程</strong>，fork操作过程中父进程会阻塞。</li><li>父进程 <strong>fork 完成后</strong>，父进程继续接收并处理客户端的请求，而<strong>子进程开始将内存中的数据写进硬盘的临时文件</strong>；</li><li>当子进程写完所有数据后会用该临时文件替换旧的 RDB 文件。</li></ul><p><strong>触发 RDB 持久化的方式</strong>：</p><ul><li><strong>手动触发</strong>：用户执行 SAVE 或 BGSAVE 命令。 <strong>SAVE</strong> 命令执行快照的过程会<strong>阻塞所有客户端的请求</strong>，应避免在生产环境使用此命令。 <strong>BGSAVE 命令</strong>可以在后台<strong>异步进行快照操作</strong>，快照的同时服务器还可以继续响应客户端的请求，因此需要手动执行快照时推荐使用 BGSAVE 命令。</li><li><strong>被动触发</strong>： <ul><li>配置文件：根据配置规则进行自动快照，如 SAVE 100 10 ，100秒内至少有10个键被修改则进行快照。</li><li>主从全量复制：如果从节点执行全量复制操作，主节点会自动执行 BGSAVE 生成 RDB 文件并发送给从节点。</li><li>执行shutdown命令（且当前没有开启AOF持久化功能）。</li></ul></li></ul><p>Redis启动时会读取RDB快照文件，将数据从硬盘载入内存。通过 RDB 方式的持久化，一旦Redis异常退出，就会丢失最近一次持久化以后更改的数据。</p><p><strong>优点</strong>：</p><ol><li>Redis 加载 RDB 恢复数据远远快于 AOF 的方式【性能高】。</li><li>使用单独子进程来进行持久化，主进程不会进行任何 IO 操作，保证了 Redis 的高性能。</li></ol><p><strong>缺点</strong>：</p><ol><li>RDB方式数据无法做到实时持久化。因为 BGSAVE 每次运行都要执行 fork 操作创建子进程，属于重量级操作，频繁执行成本比较高。【<strong>实时性差，bgsave命令每次会创建子进程，属于重量级操作</strong>】</li><li>RDB 文件使用特定二进制格式保存，Redis 版本升级过程中有多个格式的 RDB 版本，存在老版本Redis 无法兼容新版 RDB 格式的问题。【<strong>兼容问题</strong>】</li></ol><blockquote><p>文件配置</p></blockquote><p>快照持久化是 <strong>Redis 默认采用的持久化方式</strong>，在 <code>redis.conf</code> 配置文件中默认有此下配置：</p><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>save 900 1           #在900秒(15分钟)之后，如果至少有1个key发生变化，Redis就会自动触发BGSAVE命令创建快照。

save 300 10          #在300秒(5分钟)之后，如果至少有10个key发生变化，Redis就会自动触发BGSAVE命令创建快照。

save 60 10000        #在60秒(1分钟)之后，如果至少有10000个key发生变化，Redis就会自动触发BGSAVE命令创建快照。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_3、什么是-aof-持久化" tabindex="-1"><a class="header-anchor" href="#_3、什么是-aof-持久化" aria-hidden="true">#</a> 3、什么是 AOF 持久化？</h3><p>与快照持久化相比，<strong>AOF 持久化的实时性更好，因此已成为主流的持久化方案</strong>。</p><p>默认情况下 Redis 没有开启 AOF（append only file）方式的持久化，可以通过 appendonly 参数开启：</p><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>appendonly yes
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令，Redis 就会将该命令写入到内存缓存 <code>server.aof_buf</code> 中，然后再根据 <code>appendfsync</code> 配置来决定何时将其同步到硬盘中的 AOF 文件。</p><p>开启AOF方式持久化后每执行一条写命令，Redis就会将该命令写进 <code>aof_buf </code>缓冲区，AOF缓冲区根据对应的策略向硬盘做同步操作。</p><p>AOF 文件的保存位置和 RDB 文件的位置相同，都是通过 dir 参数设置的，默认的文件名是 <code>appendonly.aof</code>。</p><p><strong>默认情况下系统每30秒会执行一次同步操作</strong>。为了防止缓冲区数据丢失，可以在Redis写入AOF文件后主动要求系统将缓冲区数据同步到硬盘上。可以通过 appendfsync 参数设置同步的时机。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code>appendfsync always    <span class="token comment">#每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度</span>
appendfsync everysec  <span class="token comment">#每秒钟同步一次，显式地将多个写命令同步到硬盘</span>
appendfsync no        <span class="token comment">#让操作系统决定何时进行同步</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>为了兼顾数据和写入性能，用户可以考虑 <code>appendfsync everysec</code> 选项 ，让 Redis 每秒同步一次 AOF 文件，Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃，用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候，Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。</p><p><strong>执行流程</strong>：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206251934037.png" alt="image-20220625193422951" loading="lazy"></p><p>1、所有的写入命令会追加到 AOP 缓冲区中。</p><p>2、AOF 缓冲区根据对应的策略向硬盘同步。</p><p>3、随着 AOF 文件越来越大，需要定期对 AOF 文件进行重写，达到压缩文件体积的目的。AOF文件重 写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。</p><p>4、当 Redis 服务器重启时，可以加载 AOF 文件进行数据恢复。</p><p><strong>优点</strong>：</p><ol><li>AOF可以更好的保护数据不丢失，可以配置 AOF 每秒执行一次 fsync 操作，如果Redis进程挂掉，最多丢失1秒的数据。</li><li>AOF以 append-only 的模式写入，所以<strong>没有磁盘寻址的开销</strong>，写入性能非常高。</li></ol><p><strong>缺点</strong>：</p><ol><li>对于同一份文件AOF文件比RDB数据快照要大。</li><li>数据恢复比较慢。</li></ol><h3 id="_4、rdb与aof如何选择" tabindex="-1"><a class="header-anchor" href="#_4、rdb与aof如何选择" aria-hidden="true">#</a> 4、RDB与AOF如何选择？</h3><p>通常来说，应该同时使用两种持久化方案，以保证数据安全。</p><ul><li>如果数据不敏感，且可以从其他地方重新生成，可以关闭持久化。</li><li>如果数据比较重要，且能够承受几分钟的数据丢失，比如缓存等，只需要使用RDB即可。</li><li>如果是用做内存数据，要使用Redis的持久化，建议是RDB和AOF都开启。</li><li>如果只用AOF，优先使用everysec的配置选择，因为它在可靠性和性能之间取了一个平衡。</li></ul><p>当RDB与AOF两种方式都开启时，Redis会优先使用<code>AOF</code>恢复数据，因为<strong>AOF保存的文件比RDB文件更完整</strong>。</p><h3 id="_5、aof-重写了解吗" tabindex="-1"><a class="header-anchor" href="#_5、aof-重写了解吗" aria-hidden="true">#</a> 5、AOF 重写了解吗？</h3><p>AOF 重写可以产生一个新的 AOF 文件，这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样，但体积更小。</p><p>AOF 重写是一个有歧义的名字，该功能是<strong>通过读取数据库中的键值对来实现的</strong>，程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。</p><p>在执行 <code>BGREWRITEAOF</code> 命令时，Redis 服务器会<strong>维护一个 AOF 重写缓冲区</strong>，该缓冲区会在子进程创建新 AOF 文件期间，记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后，服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾，使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。最后，服务器用新的 AOF 文件替换旧的 AOF 文件，以此来完成 AOF 文件重写操作。</p><h3 id="_6、redis-4-0-对于持久化机制做了什么优化" tabindex="-1"><a class="header-anchor" href="#_6、redis-4-0-对于持久化机制做了什么优化" aria-hidden="true">#</a> 6、Redis 4.0 对于持久化机制做了什么优化？</h3><p>Redis 4.0 开始支持 RDB 和 AOF 的<strong>混合持久化</strong>（默认关闭，可以通过配置项 <code>aof-use-rdb-preamble</code> 开启）。</p><p>如果把混合持久化打开，<strong>AOF 重写</strong>的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的， AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式，可读性较差。</p><h2 id="_6、redis事务" tabindex="-1"><a class="header-anchor" href="#_6、redis事务" aria-hidden="true">#</a> 6、Redis事务</h2><h3 id="_1、如何使用-redis-事务" tabindex="-1"><a class="header-anchor" href="#_1、如何使用-redis-事务" aria-hidden="true">#</a> 1、如何使用 Redis 事务？</h3><p>使用 <code>MULTI </code>命令后可以输入多个命令。Redis 不会立即执行这些命令，而是将它们放到队列，当调用了<code>EXEC</code>命令将执行所有命令。</p><p>这个过程是这样的：</p><ol><li>开始事务（<code>MULTI</code>）。</li><li>命令入队(批量操作 Redis 的命令，先进先出（FIFO）的顺序执行)。</li><li>执行事务(<code>EXEC</code>)。</li></ol><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206252024655.png" alt="image-20220625202450592" loading="lazy"></p><p><strong>一个事务范围内某个命令出错不会影响其他命令的执行，不保证原子性</strong>：</p><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>127.0.0.1:6379&gt; multi
OK
127.0.0.1:6379&gt; set a 1
QUEUED
127.0.0.1:6379&gt; set b 1 2
QUEUED
127.0.0.1:6379&gt; set c 3
QUEUED
127.0.0.1:6379&gt; exec
1) OK
2) (error) ERR syntax error   # 可以看到第二条语法错误不会影响第1、第3条
3) OK
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote><p>DISCARD命令：取消事务</p></blockquote><p>你也可以通过<code>DISCARD</code> 命令取消一个事务，它会清空事务队列中保存的所有命令。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token operator">&gt;</span> multi
OK
<span class="token operator">&gt;</span> <span class="token builtin class-name">set</span> gender <span class="token number">1</span>
QUEUED
<span class="token operator">&gt;</span> DISCARD   <span class="token comment">#  在事务中执行丢弃队列操作</span>
QUEUED
<span class="token operator">&gt;</span> <span class="token builtin class-name">exec</span>
ReplyError: ERR EXEC without MULTI
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote><p>watch命令</p></blockquote><p><code>WATCH </code>命令用于监听指定的键，当调用 EXEC 命令执行事务时，如果一个被 WATCH 命令监视的键被修改的话，整个事务都不会执行（类似于悲观锁），直接返回失败。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token operator">&gt;</span> get name
null
<span class="token operator">&gt;</span> <span class="token function">watch</span> name   <span class="token comment"># 在事务开始前观测name结点</span>
OK
<span class="token operator">&gt;</span> <span class="token builtin class-name">set</span> name <span class="token number">1</span>
OK
<span class="token operator">&gt;</span> multi
OK
<span class="token operator">&gt;</span> <span class="token builtin class-name">set</span> name <span class="token number">2</span>  <span class="token comment"># 事务中set操作</span>
QUEUED
<span class="token operator">&gt;</span> <span class="token builtin class-name">set</span> gender <span class="token number">1</span>
QUEUED
<span class="token operator">&gt;</span> <span class="token builtin class-name">exec</span>     <span class="token comment"># 提交事务直接结束</span>
null
<span class="token operator">&gt;</span> get gender
null
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>比如上面的代码中：</p><ol><li><p>watch name 开启了对 name 这个 key 的监控</p></li><li><p>修改 name 的值</p></li><li><p>开启事务a</p></li><li><p>在事务a中设置了 name 和 gender 的值</p></li><li><p>使用 EXEC 命令进提交事务</p></li><li><p>使用命令 get gender 发现不存在，即事务a没有执行</p></li></ol><p>使用 <code>UNWATCH</code> 可以取消 <code>WATCH </code>命令对 <code>key </code>的监控，所有监控锁将会被取消。</p><h3 id="_2、redis-支持原子性吗" tabindex="-1"><a class="header-anchor" href="#_2、redis-支持原子性吗" aria-hidden="true">#</a> 2、Redis 支持原子性吗？</h3><p>Redis 的事务和我们平时理解的关系型数据库的事务不同。我们知道事务具有四大特性： <strong>1. 原子性</strong>，<strong>2. 隔离性</strong>，<strong>3. 持久性</strong>，<strong>4. 一致性</strong>。</p><ol><li><strong>原子性（Atomicity）：</strong> 事务是最小的执行单位，不允许分割。事务的原子性确保动作要么全部完成，要么完全不起作用；</li><li><strong>隔离性（Isolation）：</strong> 并发访问数据库时，一个用户的事务不被其他事务所干扰，各并发事务之间数据库是独立的；</li><li><strong>持久性（Durability）：</strong> 一个事务被提交之后。它对数据库中数据的改变是持久的，即使数据库发生故障也不应该对其有任何影响。</li><li><strong>一致性（Consistency）：</strong> 执行事务前后，数据保持一致，多个事务对同一个数据读取的结果是相同的；</li></ol><p>Redis 事务在运行错误的情况下，除了执行过程中出现错误的命令外，其他命令都能正常执行。并且，Redis 是<strong>不支持回滚（roll back）<strong>操作的。因此，Redis 事务其实是</strong>不满足原子性</strong>的（而且<strong>不满足持久性</strong>）。</p><ul><li>Redis 官网也解释了自己为啥<strong>不支持回滚</strong>。简单来说就是 Redis 开发者们觉得没必要支持回滚，这样更简单便捷并且性能更好。Redis 开发者觉得即使命令执行错误也应该在开发过程中就被发现而不是生产过程中。</li></ul><p>可以将 Redis 中的事务就理解为 ：<strong>Redis 事务提供了一种将多个命令请求打包的功能。然后，再按顺序执行打包的所有命令，并且不会被中途打断。</strong></p><p>除了不满足原子性之外，事务中的每条命令都会与 Redis 服务器进行网络交互，这是比较浪费资源的行为。明明一次批量执行多个命令就可以了，这种操作实在是看不懂。</p><p>因此，Redis 事务是不建议在日常开发中使用的。</p><h3 id="_3、如何解决-redis-事务的缺陷" tabindex="-1"><a class="header-anchor" href="#_3、如何解决-redis-事务的缺陷" aria-hidden="true">#</a> 3、如何解决 Redis 事务的缺陷？</h3><p>Redis 从 2.6 版本开始支持执行 Lua 脚本，它的功能和事务非常类似。我们可以利用 Lua 脚本来批量执行多条 Redis 命令，这些 Redis 命令会被提交到 Redis 服务器一次性执行完成，大幅减小了网络开销。</p><p>一段 Lua 脚本可以视作一条命令执行，一段 Lua 脚本执行过程中不会有其他脚本或 Redis 命令同时执行，保证了操作不会被其他指令插入或打扰。</p><p>如果 Lua 脚本运行时出错并中途结束，出错之后的命令是不会被执行的。并且，出错之前执行的命令是无法被撤销的。因此，严格来说，通过 Lua 脚本来批量执行 Redis 命令也是不满足原子性的。</p><p>另外，Redis 7.0 新增了 Redis functions特性，你可以将 Redis functions 看作是比 Lua 更强大的脚本。</p><h2 id="_7、redis-性能优化" tabindex="-1"><a class="header-anchor" href="#_7、redis-性能优化" aria-hidden="true">#</a> 7、Redis 性能优化</h2><h3 id="_1、redis-bigkey" tabindex="-1"><a class="header-anchor" href="#_1、redis-bigkey" aria-hidden="true">#</a> 1、Redis bigkey</h3><h4 id="_1、什么是-bigkey" tabindex="-1"><a class="header-anchor" href="#_1、什么是-bigkey" aria-hidden="true">#</a> 1、什么是 bigkey？</h4><p>简单来说，<strong>如果一个 key 对应的 value 所占用的内存比较大</strong>，那这个 key 就可以看作是 bigkey。具体多大才算大呢？有一个不是特别精确的参考标准：string 类型的 value 超过 10 kb，复合类型的 value 包含的元素超过 5000 个（对于复合类型的 value 来说，不一定包含的元素越多，占用的内存就越多）。</p><h4 id="_2、bigkey-有什么危害" tabindex="-1"><a class="header-anchor" href="#_2、bigkey-有什么危害" aria-hidden="true">#</a> 2、bigkey 有什么危害？</h4><p>除了会消耗更多的内存空间，bigkey 对性能也会有比较大的影响。</p><p>因此，我们应该尽量避免写入 bigkey！</p><h4 id="_3、如何发现-bigkey" tabindex="-1"><a class="header-anchor" href="#_3、如何发现-bigkey" aria-hidden="true">#</a> 3、如何发现 bigkey？</h4><p>1、使用 Redis 自带的 <code>--bigkeys</code> 参数来查找。</p><p>2、分析 RDB 文件：通过分析 RDB 文件来找出 big key。这种方案的前提是你的 Redis 采用的是 RDB 持久化。</p><h3 id="_2、大量-key-集中过期问题" tabindex="-1"><a class="header-anchor" href="#_2、大量-key-集中过期问题" aria-hidden="true">#</a> 2、大量 key 集中过期问题</h3><p>对于过期 key，Redis 采用的是 <strong>定期删除+惰性/懒汉式删除</strong> 策略。</p><p>定期删除执行过程中，如果突然遇到大量过期 key 的话，客户端请求必须等待定期清理过期 key 任务线程执行完成，因为这个这个定期任务线程是在 Redis 主线程中执行的。这就导致客户端请求没办法被及时处理，响应速度会比较慢。</p><p><strong>如何解决呢</strong>？下面是两种常见的方法：</p><ol><li>给 key 设置随机过期时间。</li><li>开启 <code>lazy-free</code>（惰性删除/延迟释放） 。<code>lazy-free</code> 特性是 Redis 4.0 开始引入的，指的是让 Redis <strong>采用异步方式</strong>延迟释放 key 使用的内存，将该操作交给单独的子线程处理，避免阻塞主线程。</li></ol><p>个人建议不管是否开启<code> lazy-free</code>，我们都尽量给 key 设置随机过期时间。</p><h2 id="_8、redis-生产问题" tabindex="-1"><a class="header-anchor" href="#_8、redis-生产问题" aria-hidden="true">#</a> 8、Redis 生产问题</h2><h3 id="_1、缓存穿透" tabindex="-1"><a class="header-anchor" href="#_1、缓存穿透" aria-hidden="true">#</a> 1、缓存穿透</h3><h4 id="什么是缓存穿透" tabindex="-1"><a class="header-anchor" href="#什么是缓存穿透" aria-hidden="true">#</a> 什么是缓存穿透？</h4><p>缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中，导致请求直接到了数据库上，根本没有经过缓存这一层。</p><p>举个例子：某个黑客故意制造我们缓存中不存在的 key 发起大量请求，导致大量请求落到数据库。</p><h4 id="解决方案" tabindex="-1"><a class="header-anchor" href="#解决方案" aria-hidden="true">#</a> 解决方案</h4><p>最基本的就是首先做好参数校验，一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。</p><p>1）缓存无效 key：如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间，具体命令如下： <code>SET key value EX 10086</code> 。这种方式可以解决请求的 key 变化不频繁的情况，如果黑客恶意攻击，每次构建不同的请求 key，会导致 Redis 中缓存大量无效的 key 。很明显，这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话，尽量将无效的 key 的过期时间设置短一点比如 1 分钟。</p><ul><li>一般情况下我们是这样设计 key 的： <code>表名:列名:主键名:主键值</code></li></ul><p>Java代码：先走缓存，再走数据库，若是数据库差不到，设置一个缓存数据。</p><p>2）布隆过滤器：<a href="https://blog.csdn.net/wang0112233/article/details/123665461" target="_blank" rel="noopener noreferrer">Redis 之布隆过滤器<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p><p>布隆过滤器是一个非常神奇的数据结构，通过它我们可以非常方便地<strong>判断一个给定数据是否存在于海量数据中</strong>。我们需要的就是判断 key 是否合法，有没有感觉布隆过滤器就是我们想要找的那个“人”。</p><p>具体是这样做的：把所有可能存在的请求的值都存放在布隆过滤器中，当用户请求过来，先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话，直接返回请求参数错误信息给客户端，存在的话才会走下面的流程。</p><p>加入布隆过滤器之后的缓存处理流程图如下：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171141077.png" alt="image-20220617114114978" loading="lazy"></p><p>但是，需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是： <strong>布隆过滤器说某个元素存在，小概率会误判。布隆过滤器说某个元素不在，那么这个元素一定不在。</strong></p><p>我们先来看一下，<strong>当一个元素加入布隆过滤器中的时候，会进行哪些操作：</strong></p><ol><li>使用布隆过滤器中的哈希函数对元素值进行计算，得到哈希值（有几个哈希函数得到几个哈希值）。</li><li>根据得到的哈希值，在位数组中把对应下标的值置为 1。</li></ol><p>我们再来看一下，<strong>当我们需要判断一个元素是否存在于布隆过滤器的时候，会进行哪些操作：</strong></p><ol><li>对给定元素再次进行相同的哈希计算；</li><li>得到值之后判断位数组中的每个元素是否都为 1，如果值都为 1，那么说明这个值在布隆过滤器中，如果存在一个值不为 1，说明该元素不在布隆过滤器中。</li></ol><p>然后，一定会出现这样一种情况：<strong>不同的字符串可能哈希出来的位置相同。</strong> （可以适当增加位数组大小或者调整我们的哈希函数来降低概率）</p><h3 id="_2、缓存雪崩" tabindex="-1"><a class="header-anchor" href="#_2、缓存雪崩" aria-hidden="true">#</a> 2、缓存雪崩</h3><h4 id="什么是缓存雪崩" tabindex="-1"><a class="header-anchor" href="#什么是缓存雪崩" aria-hidden="true">#</a> 什么是缓存雪崩？</h4><p>实际上，缓存雪崩描述的就是这样一个简单的场景：<strong>缓存在同一时间大面积的失效，后面的请求都直接落到了数据库上，造成数据库短时间内承受大量请求。</strong> 这就好比雪崩一样，摧枯拉朽之势，数据库的压力可想而知，可能直接就被这么多请求弄宕机了。</p><p>举个例子：系统的缓存模块出了问题比如宕机导致不可用。造成系统的所有访问，都要走数据库。</p><p>还有一种缓存雪崩的场景是：<strong>有一些被大量访问数据（热点缓存）在某一时刻大面积失效，导致对应的请求直接落到了数据库上。</strong> 这样的情况，有下面几种解决办法：</p><p>举个例子 ：秒杀开始 12 个小时之前，我们统一存放了一批商品到 Redis 中，设置的缓存过期时间也是 12 个小时，那么秒杀开始的时候，这些秒杀的商品的访问直接就失效了。导致的情况就是，相应的请求直接就落到了数据库上，就像雪崩一样可怕。</p><h4 id="解决方案-1" tabindex="-1"><a class="header-anchor" href="#解决方案-1" aria-hidden="true">#</a> 解决方案</h4><p><strong>针对 Redis 服务不可用的情况：</strong></p><ol><li>采用 Redis 集群，避免单机出现问题整个缓存服务都没办法使用。</li><li>限流，避免同时处理大量的请求。</li></ol><p><strong>针对热点缓存失效的情况：</strong></p><ol><li>设置不同的失效时间比如随机设置缓存的失效时间。</li><li>缓存永不失效。</li></ol><h3 id="_3、缓存击穿-分布式项目情况下涉及到分布式锁" tabindex="-1"><a class="header-anchor" href="#_3、缓存击穿-分布式项目情况下涉及到分布式锁" aria-hidden="true">#</a> 3、缓存击穿（分布式项目情况下涉及到分布式锁）</h3><p><strong>缓存击穿</strong>：大量的请求同时查询一个 key 时，此时这个 key 正好失效了，就会导致大量的请求都落到数据库。缓存击穿是查询缓存中失效的 key，而缓存穿透是查询不存在的 key。</p><p><strong>解决方案（分布式项目）</strong>：加分布式锁，第一个请求的线程可以拿到锁，拿到锁的线程查询到了数据之后设置缓存，其他的线程获取锁失败会等待50ms然后重新到缓存取数据，这样便可以避免大量的请求落到数据库。</p><blockquote><p>分布式锁：<a href="https://blog.csdn.net/zxd1435513775/article/details/122194202" target="_blank" rel="noopener noreferrer">Redis实现分布式锁<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>：多种方式层层递进；<a href="https://blog.csdn.net/m0_45245077/article/details/124565014" target="_blank" rel="noopener noreferrer">基于Redis（SETNX）实现分布式锁，案例：解决高并发下的订单超卖，秒杀<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p><p>业界知名解决方案：RedisSon</p></blockquote><p><a href="https://blog.csdn.net/liuxl57805678/article/details/103199977" target="_blank" rel="noopener noreferrer">Redis分布式锁 setnx命令如何设置key的失效时间<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a></p><p>一般思路是：setnx命令后，然后设置一个expire。但是这是两个命令，可以使用<code>set</code>命令完成<code>setnx</code>和<code>expire</code>的操作，并且这种操作是原子操作：</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token builtin class-name">set</span> key value <span class="token punctuation">[</span>EX seconds<span class="token punctuation">]</span> <span class="token punctuation">[</span>PX milliseconds<span class="token punctuation">]</span> <span class="token punctuation">[</span>NX<span class="token operator">|</span>XX<span class="token punctuation">]</span>
EX seconds：设置失效时长，单位秒
PX milliseconds：设置失效时长，单位毫秒
NX：key不存在时设置value，成功返回OK，失败返回<span class="token punctuation">(</span>nil<span class="token punctuation">)</span>
XX：key存在时设置value，成功返回OK，失败返回<span class="token punctuation">(</span>nil<span class="token punctuation">)</span>

<span class="token comment"># nx表示key不存在设置value，px 100表示100毫秒</span>
<span class="token builtin class-name">set</span> name changlu px <span class="token number">100</span> nx
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_4、如何保证缓存和数据库数据的一致性" tabindex="-1"><a class="header-anchor" href="#_4、如何保证缓存和数据库数据的一致性" aria-hidden="true">#</a> 4、如何保证缓存和数据库数据的一致性？</h3><p>1、先删除缓存再更新数据库</p><p>进行更新操作时，先删除缓存，然后更新数据库，后续的请求再次读取时，会从数据库读取后再将新数据更新到缓存。</p><p>存在的问题：删除缓存数据之后，更新数据库完成之前，这个时间段内如果有新的读请求过来，就会从数据库读取旧数据重新写到缓存中，再次造成不一致，并且后续读的都是旧数据。</p><p>2、先更新数据库再删除缓存</p><p>进行更新操作时，先更新MySQL，成功之后，删除缓存，后续读取请求时再将新数据回写缓存。</p><p>存在的问题：更新MySQL和删除缓存这段时间内，请求读取的还是缓存的旧数据，不过等数据库更新完成，就会恢复一致，影响相对比较小。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206260814289.png" alt="image-20220626081402179" loading="lazy"></p><p>3、异步更新缓存</p><p>数据库的更新操作完成后不直接操作缓存，而是把这个操作命令封装成消息<strong>扔到消息队列中</strong>，然后由Redis自己去消费更新数据，消息队列可以保证数据操作顺序一致性，确保缓存系统的数据正常。</p><h3 id="_5、redis-怎么实现消息队列" tabindex="-1"><a class="header-anchor" href="#_5、redis-怎么实现消息队列" aria-hidden="true">#</a> 5、Redis 怎么实现消息队列？</h3><p><strong>方案</strong>：使用一个列表，让生产者将任务使用LPUSH命令放进列表，消费者不断用RPOP从列表取出任务。当 Rpop 没有消息的时候，要适当 sleep 一会再重试。</p><p><strong>如果对方追问可不可以不用 sleep 呢？</strong></p><ul><li>list 还有个指令叫 bRpop，在没有消息的时候，它会阻塞住直到消息到来。 <ul><li>BRPOP和RPOP命令相似，唯一的区别就是当列表没有元素时BRPOP命令会一直阻塞连接，直到有新元素加入。</li></ul></li></ul><div class="language-text ext-text line-numbers-mode"><pre class="language-text"><code>bRpop queue:1 queue:2 queue:3 0
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p><strong>如果对方追问能不能生产一次消费多次呢？</strong></p><ul><li>使用 pub/sub 主题订阅者模式，可以实现1:N 的消息队列。</li></ul><p>发布/订阅模式：</p><p>PSUBSCRIBE channel?* 按照规则订阅。 PUNSUBSCRIBE channel?* 退订通过PSUBSCRIBE命令按照某种规则订阅的频道。其中订阅规则要进行严格的字符串匹配， PUNSUBSCRIBE * 无法退订 channel?*规则。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code>PUBLISH channel1 hi
SUBSCRIBE channel1
UNSUBSCRIBE channel1 //退订通过SUBSCRIBE命令订阅的频道。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>缺点：在消费者下线的情况下，生产的消息会丢失。</p><p><strong>如果对方追问 pub/sub 有什么缺点？</strong></p><ul><li>在消费者下线的情况下，生产的消息会丢失，得使用专业的消息队列如 RabbitMQ等。</li></ul><p><strong>如果对方追问 redis 如何实现延时队列？</strong></p><ul><li>使用 sortedset，拿时间戳作为score，消息内容作为 key 调用 zadd 来生产消息，消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。</li></ul><h2 id="_9、3种常用的缓存读写策略详解" tabindex="-1"><a class="header-anchor" href="#_9、3种常用的缓存读写策略详解" aria-hidden="true">#</a> 9、3种常用的缓存读写策略详解</h2><h3 id="cache-aside-pattern-旁路缓存模式" tabindex="-1"><a class="header-anchor" href="#cache-aside-pattern-旁路缓存模式" aria-hidden="true">#</a> Cache Aside Pattern （旁路缓存模式）</h3><p>Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式，比较适合读请求比较多的场景。</p><p>Cache Aside Pattern 中服务端需要同时维系 DB 和 cache，并且是以 DB 的结果为准。</p><p>下面我们来看一下这个策略模式下的缓存读写步骤。</p><p><strong>写</strong> ：</p><ul><li>先更新 DB</li><li>然后直接删除 cache 。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171304761.png" alt="image-20220617130450689" loading="lazy"></p><p><strong>读</strong> :</p><ul><li>从 cache 中读取数据，读取到就直接返回</li><li>cache中读取不到的话，就从 DB 中读取数据返回</li><li>再把数据放到 cache 中。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171318374.png" alt="image-20220617131838307" loading="lazy"></p><p>比如说面试官很可能会追问：“<strong>在写数据的过程中，可以先删除 cache ，后更新 DB 么？</strong>”</p><p><strong>答案：</strong> 那肯定是不行的！因为这样可能会造成<strong>数据库（DB）和缓存（Cache）数据不一致</strong>的问题。为什么呢？比如说请求1 先写数据A，请求2随后读数据A的话就很有可能产生数据不一致性的问题。这个过程可以简单描述为：</p><blockquote><p>请求1先把cache中的A数据删除 -&gt; 请求2从DB中读取数据-&gt;请求1再把DB中的A数据更新。</p></blockquote><p>当你这样回答之后，面试官可能会紧接着就追问：“<strong>在写数据的过程中，先更新DB，后删除cache就没有问题了么？</strong>”</p><p><strong>答案：</strong> 理论上来说还是可能会出现数据不一致性的问题，不过概率非常小，因为缓存的写入速度是比数据库的写入速度快很多！</p><p>比如请求1先读数据 A，请求2随后写数据A，并且数据A不在缓存中的话也有可能产生数据不一致性的问题。这个过程可以简单描述为：</p><blockquote><p>请求1从DB读数据A-&gt;请求2写更新数据 A 到数据库并把删除cache中的A数据-&gt;请求1将数据A写入cache。</p></blockquote><p>现在我们再来分析一下 <strong>Cache Aside Pattern 的缺陷</strong>。</p><p><strong>缺陷1：首次请求数据一定不在 cache 的问题</strong></p><p>解决办法：可以将热点数据可以提前放入cache 中。</p><p><strong>缺陷2：写操作比较频繁的话导致cache中的数据会被频繁被删除，这样会影响缓存命中率 。</strong></p><p>解决办法：</p><ul><li>数据库和缓存数据强一致场景 ：更新DB的时候同样更新cache，不过我们需要加一个锁/分布式锁来保证更新cache的时候不存在线程安全问题。</li><li>可以短暂地允许数据库和缓存数据不一致的场景 ：更新DB的时候同样更新cache，但是给缓存加一个比较短的过期时间，这样的话就可以保证即使数据不一致的话影响也比较小。</li></ul><h3 id="read-write-through-pattern-读写穿透" tabindex="-1"><a class="header-anchor" href="#read-write-through-pattern-读写穿透" aria-hidden="true">#</a> Read/Write Through Pattern（读写穿透）</h3><p>Read/Write Through Pattern 中服务端把 <strong>cache 视为主要数据存储，从中读取数据并将数据写入其中</strong>。cache 服务负责将此数据读取和写入 DB，从而减轻了应用程序的职责。</p><p>这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响，大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入DB的功能。</p><p><strong>写（Write Through）：</strong></p><ul><li>先查 cache，cache 中不存在，直接更新 DB。</li><li>cache 中存在，则先更新 cache，然后 cache 服务自己更新 DB（<strong>同步更新 cache 和 DB</strong>）。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171327996.png" alt="image-20220617132756930" loading="lazy"></p><p><strong>读(Read Through)：</strong></p><ul><li>从 cache 中读取数据，读取到就直接返回 。</li><li>读取不到的话，先从 DB 加载，写入到 cache 后返回响应。</li></ul><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171329636.png" alt="image-20220617132901582" loading="lazy"></p><p>Read-Through Pattern 实际只是在 Cache-Aside Pattern 之上进行了封装。在 Cache-Aside Pattern 下，发生读请求的时候，如果 cache 中不存在对应的数据，是由客户端自己负责把数据写入 cache，而 Read-Through Pattern 则是 cache 服务自己来写入缓存的，这对客户端是透明的。</p><p>和 Cache Aside Pattern 一样， Read-Through Pattern 也有首次请求数据一定不再 cache 的问题，对于热点数据可以提前放入缓存中。</p><h3 id="write-behind-pattern-异步缓存写入" tabindex="-1"><a class="header-anchor" href="#write-behind-pattern-异步缓存写入" aria-hidden="true">#</a> Write Behind Pattern（异步缓存写入）</h3><p>Write Behind Pattern 和 Read/Write Through Pattern 很相似，两者都是由 cache 服务来负责 cache 和 DB 的读写。</p><p>但是，两个又有很大的不同：<strong>Read/Write Through 是同步更新 cache 和 DB，而 Write Behind Caching 则是只更新缓存，不直接更新 DB，而是改为异步批量的方式来更新 DB。</strong></p><p>很明显，这种方式对数据一致性带来了更大的挑战，比如cache数据可能还没异步更新DB的话，cache服务可能就就挂掉了。</p><p>这种策略在我们平时开发过程中也非常非常少见，但是不代表它的应用场景少，比如消息队列中消息的异步写入磁盘、MySQL 的 InnoDB Buffer Pool 机制都用到了这种策略。</p><p>Write Behind Pattern 下 DB 的写性能非常高，非常适合一些数据经常变化又对数据一致性要求没那么高的场景，比如浏览量、点赞量。</p><h2 id="_10、redis-内存碎片详解" tabindex="-1"><a class="header-anchor" href="#_10、redis-内存碎片详解" aria-hidden="true">#</a> 10、Redis 内存碎片详解</h2><h3 id="_1、什么是内存碎片" tabindex="-1"><a class="header-anchor" href="#_1、什么是内存碎片" aria-hidden="true">#</a> 1、什么是内存碎片?</h3><p>你可以将内存碎片简单地理解为那些不可用的空闲内存。</p><p>举个例子：操作系统为你分配了 32 字节的连续内存空间，而你存储数据实际只需要使用 24 字节内存空间，那这多余出来的 8 字节内存空间如果后续没办法再被分配存储其他数据的话，就可以被称为内存碎片。</p><p>Redis 内存碎片虽然不会影响 Redis 性能，但是会增加内存消耗。</p><h3 id="_2、为什么会有-redis-内存碎片" tabindex="-1"><a class="header-anchor" href="#_2、为什么会有-redis-内存碎片" aria-hidden="true">#</a> 2、为什么会有 Redis 内存碎片?</h3><p>Redis 内存碎片产生比较常见的 2 个原因：</p><p><strong>1、Redis 存储存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间。</strong></p><p>Redis 使用 <code>zmalloc</code> 方法(Redis 自己实现的内存分配方法)进行内存分配的时候，除了要分配 <code>size</code> 大小的内存之外，还会多分配 <code>PREFIX_SIZE</code> 大小的内存。</p><p>另外，Redis 可以使用多种内存分配器来分配内存（ libc、jemalloc、tcmalloc），默认使用jemalloc，而 jemalloc 按照一系列固定的大小（8 字节、16 字节、32 字节......）来分配内存的。jemalloc 划分的内存单元如下图所示：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171340626.png" alt="image-20220617134022562" loading="lazy"></p><p>当程序申请的内存最接近某个固定值时，jemalloc 会给它分配相应大小的空间，就比如说程序需要申请 17 字节的内存，jemalloc 会直接给它分配 32 字节的内存，这样会导致有 15 字节内存的浪费。不过，jemalloc 专门针对内存碎片问题做了优化，一般不会存在过度碎片化的问题。</p><p><strong>2、频繁修改 Redis 中的数据也会产生内存碎片。</strong></p><p>当 Redis 中的某个数据删除时，Redis 通常不会轻易释放内存给操作系统。</p><p>这个在 Redis 官方文档中也有对应的原话:</p><h3 id="_3、如何查看-redis-内存碎片的信息" tabindex="-1"><a class="header-anchor" href="#_3、如何查看-redis-内存碎片的信息" aria-hidden="true">#</a> 3、如何查看 Redis 内存碎片的信息？</h3><p>使用 <code>info memory</code> 命令即可查看 Redis 内存相关的信息。下图中每个参数具体的含义。</p><p>Redis 内存碎片率的计算公式：<code>mem_fragmentation_ratio</code> （内存碎片率）= <code>used_memory_rss</code> (操作系统实际分配给 Redis 的物理内存空间大小)/ <code>used_memory</code>(Redis 内存分配器为了存储数据实际申请使用的内存空间大小)</p><p>也就是说，<code>mem_fragmentation_ratio</code> （内存碎片率）的值越大代表内存碎片率越严重。</p><p>一定不要误认为<code>used_memory_rss</code> 减去 <code>used_memory</code>值就是内存碎片的大小！！！这不仅包括内存碎片，还包括其他进程开销，以及共享库、堆栈等的开销。</p><p>很多小伙伴可能要问了：“多大的内存碎片率才是需要清理呢？”。</p><p>通常情况下，我们认为 <code>mem_fragmentation_ratio &gt; 1.5</code> 的话才需要清理内存碎片。 <code>mem_fragmentation_ratio &gt; 1.5</code> 意味着你使用 Redis 存储实际大小 2G 的数据需要使用大于 3G 的内存。</p><p>如果想要快速查看内存碎片率的话，你还可以通过下面这个命令：</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token operator">&gt;</span> redis-cli -p <span class="token number">6379</span> info <span class="token operator">|</span> <span class="token function">grep</span> mem_fragmentation_ratio
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>另外，内存碎片率可能存在小于 1 的情况。这种情况我在日常使用中还没有遇到过。</p><h3 id="_4、如何清理-redis-内存碎片" tabindex="-1"><a class="header-anchor" href="#_4、如何清理-redis-内存碎片" aria-hidden="true">#</a> 4、如何清理 Redis 内存碎片？</h3><p>Redis4.0-RC3 版本以后自带了内存整理，可以避免内存碎片率过大的问题。</p><p>直接通过 <code>config set</code> 命令将 <code>activedefrag</code> 配置项设置为 <code>yes</code> 即可。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code>config <span class="token builtin class-name">set</span> activedefrag <span class="token function">yes</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>具体什么时候清理需要通过下面两个参数控制：</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token comment"># 内存碎片占用空间达到 500mb 的时候开始清理</span>
config <span class="token builtin class-name">set</span> active-defrag-ignore-bytes 500mb
<span class="token comment"># 内存碎片率大于 1.5 的时候开始清理</span>
config <span class="token builtin class-name">set</span> active-defrag-threshold-lower <span class="token number">50</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>通过 Redis 自动内存碎片清理机制可能会对 Redis 的性能产生影响，我们可以通过下面两个参数来减少对 Redis 性能的影响：</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code><span class="token comment"># 内存碎片清理所占用 CPU 时间的比例不低于 20%</span>
config <span class="token builtin class-name">set</span> active-defrag-cycle-min <span class="token number">20</span>
<span class="token comment"># 内存碎片清理所占用 CPU 时间的比例不高于 50%</span>
config <span class="token builtin class-name">set</span> active-defrag-cycle-max <span class="token number">50</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>另外，重启节点可以做到内存碎片重新整理。如果你采用的是高可用架构的 Redis 集群的话，你可以将碎片率过高的主节点转换为从节点，以便进行安全重启。</p><h2 id="_11、redis集群" tabindex="-1"><a class="header-anchor" href="#_11、redis集群" aria-hidden="true">#</a> 11、Redis集群</h2><h3 id="常见问题" tabindex="-1"><a class="header-anchor" href="#常见问题" aria-hidden="true">#</a> 常见问题</h3><p>1、说说 Redis 哈希槽的概念？</p><ul><li>Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念，Redis 集群有16384 个哈希槽，每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽，集群的每个节点负责一部分 hash 槽。</li></ul><p>2、Redis 集群的主从复制模型是怎样的？</p><ul><li>为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用，所以集群使用了主从复制模型,每个节点都会有 N-1 个复制品。</li></ul><p>3、Redis 集群会有写操作丢失吗？为什么？</p><ul><li>Redis 并不能保证数据的强一致性，这意味这在实际中集群在特定的条件下可能会丢失写操作。</li></ul><p>4、Redis集群之间如何复制？</p><ul><li>异步复制</li></ul><p>5、Redis 集群最大节点个数是多少？</p><ul><li>16384 个。</li></ul><p>6、Redis 集群如何选择数据库？</p><ul><li>Redis 集群目前无法做数据库选择，默认在 0 数据库。</li></ul><p>7、怎么测试 Redis 的连通性？</p><ul><li>ping命令</li></ul><h3 id="redis的常见部署方式" tabindex="-1"><a class="header-anchor" href="#redis的常见部署方式" aria-hidden="true">#</a> Redis的常见部署方式</h3><p>Redis的几种常见使用方式包括：</p><ul><li>单机版</li><li>Redis主从</li><li>Redis Sentinel（哨兵）</li><li>Redis Cluster</li></ul><p><strong>使用场景</strong>：</p><ul><li>单机版：很少使用。存在的问题：1、内存容量有限 2、处理能力有限 3、无法高可用。</li><li>主从模式：master 节点挂掉后，需要手动指定新的 master，可用性不高，基本不用。</li><li>哨兵模式：master 节点挂掉后，哨兵进程会主动选举新的 master，可用性高，但是每个节点存储的数据是一样的，浪费内存空间。数据量不是很多，集群规模不是很大，需要<strong>自动容错容灾</strong>的时候使用。</li><li>Redis cluster（分片）：主要是针对海量数据+高并发+高可用的场景，如果是海量数据，如果你的数据量很大，那么建议就用Redis cluster，<strong>所有主节点的容量总和就是Redis cluster可缓存的数据容量</strong>。</li></ul><h3 id="_1、何时使用redis集群方案" tabindex="-1"><a class="header-anchor" href="#_1、何时使用redis集群方案" aria-hidden="true">#</a> 1、何时使用Redis集群方案？</h3><p>单机 Redis 存在单点风险问题，如果 Redis 宕机的话，就会导致大量的请求直接打到数据库，严重的情况下，数据库很可能会直接被打死。</p><p><strong>如何保证 Redis 服务的高可用呢？</strong></p><ul><li>我们可以基于主从复制搭建一个 Redis 集群，master（主节点）主要负责处理写请求，slave（从节点）主要负责处理读请求，master 宕机的话，从 slave 中选出一台作为 master 即可！</li></ul><p><strong>基于主从的方案</strong>：Redis Sentinel（哨兵）、Redis 分片集群。</p><ul><li>Redis Sentinel（哨兵）就是一个基于主从复制的 Redis 集群解决方案。不过，Redis Sentinel 这种方案主要是<strong>提高了 Redis 服务的可用性和读吞吐量，并不能缓解写压力以及解决缓存数据量过大的问题</strong>。</li><li>如果<strong>我们的缓存的写请求比较多且需要缓存的数据比较大的话</strong>，Redis Sentinel 是没办法做到的，这个时候，我们就需要用到 Redis 分片集群了，Redis Cluster 是 Redis 官方推出的分片集群解决方案 。</li></ul><h3 id="_2、主从复制-同步" tabindex="-1"><a class="header-anchor" href="#_2、主从复制-同步" aria-hidden="true">#</a> <strong>2、主从复制（同步）</strong></h3><p>在主从复制这种方案下，我们通过 <a href="https://redis.io/docs/manual/replication/" target="_blank" rel="noopener noreferrer">Redis replication<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>（Redis 默认使用异步复制）来搭建一个一主(master)多从(slave)的 Redis 服务架构来提高可用性和读吞吐量。master 主要负责处理写请求，slave 主要负责处理读请求，master 宕机的话，从 slave 中选出一台作为 master 即可。</p><p><strong>主从复制流程</strong>：当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的，它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库，而<strong>一个从数据库只能有一个主数据库</strong>。</p><p><strong>主从复制的原理</strong>？</p><ol><li><p>当启动一个从节点时，它会发送一个 PSYNC 命令给主节点；</p></li><li><p>如果是从节点初次连接到主节点，那么会触发一次全量复制。此时主节点会启动一个后台线程，开 始生成一份 RDB 快照文件；</p></li><li><p>同时还会将从客户端 client 新收到的所有写命令缓存在内存中。 RDB 文件生成完毕后， 主节点会 将 RDB 文件发送给从节点，从节点会先将 RDB 文件写入本地磁盘，然后再从本地磁盘加载到内存</p></li><li><p>接着主节点会将内存中缓存的写命令发送到从节点，从节点同步这些数据；</p></li><li><p>如果从节点跟主节点之间网络出现故障，连接断开了，会自动重连，连接之后主节点仅会将部分缺 失的数据同步给从节点。</p></li></ol><p><strong>架构图</strong>：</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171355364.png" alt="image-20220617135502284" loading="lazy"></p><p>具体要配置多少 slave 节点呢？ 这个主要取决于读吞吐量，因为 slave 节点分担的是读请求，写请求由 master 节点负责。</p><p><strong>这样的方式有什么痛点呢？</strong> 一旦 master 宕机，slave 晋升成 master，同时需要修改应用方的主节点地址，还需要命令所有从节点去复制新的主节点，整个过程需要人工干预。人工干预大大增加了问题的处理时间以及出错的可能性，如果能够自动化地完成故障切换就好了。</p><p>接下来介绍的 Redis Sentinel（哨兵）就可以帮助我们来解决这个痛点。</p><p><strong>简单总结一下主从复制这种方案的优缺点</strong>：</p><ul><li>优点 ：master 发生宕机的话可以手动将某一台 slave 升级为 master，Redis 服务可用性提高。slave 可以分担读请求，读吞吐量大幅提高。</li><li>缺点 ：手动将 slave 升级为 master 的过程费时且容易出错，，不支持横向扩展无法缓解写压力和存储压力。</li></ul><blockquote><p>基本配置</p></blockquote><div class="language-java ext-java line-numbers-mode"><pre class="language-java"><code>redis<span class="token operator">-</span>server <span class="token comment">//启动Redis实例作为主数据库</span>
redis<span class="token operator">-</span>server <span class="token operator">--</span>port <span class="token number">6380</span> <span class="token operator">--</span>slaveof <span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token number">6379</span> <span class="token comment">//启动另一个实例作为从数据库</span>
slaveof <span class="token number">127.0</span><span class="token number">.0</span><span class="token number">.1</span> <span class="token number">6379</span>
SLAVEOF NO ONE <span class="token comment">//停止接收其他数据库的同步并转化为主数据库。</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote><p>牛客</p></blockquote><p>slave -&gt; psync -&gt; master</p><p>主从同步分为全量同步和增量同步。</p><ul><li>全量同步：从机第一次连接主机时不会携带主机id和数据偏移量，主机会对从机的主机id进行校验，如果不是则说明是第一次连接需要进行全量同步，原理就是<strong>将当前数据写到RDB文件发送给从机</strong>，从机接收到文件之后将数据读取到从机的内存中。</li><li>增量同步是第二次和之后连接才会发生，当从机第一次同步完成之后，主机在这期间数据发生变化，会将命令存储在缓冲区，当校验到从机的id正确时会获取从机的偏移量，主机从偏移量记录的命令开始将从机没同步的数据的操作命令发送给从机执行，执行完成后即完成了数据同步。</li></ul><h4 id="redis-的同步机制了解么" tabindex="-1"><a class="header-anchor" href="#redis-的同步机制了解么" aria-hidden="true">#</a> Redis 的同步机制了解么？</h4><p>Redis 可以使用主从同步，从从同步。第一次同步时，主节点做一次 bgsave，并同时将后续修改操作记录到内存 buffer，待完成后将 rdb 文件全量同步到复制节点，复制节点接受完成后将 rdb 镜像加载到内存。加载完成后，再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。</p><h3 id="_3、redis-sentinel-哨兵" tabindex="-1"><a class="header-anchor" href="#_3、redis-sentinel-哨兵" aria-hidden="true">#</a> 3、Redis Sentinel（哨兵）</h3><p>主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。</p><p>Sentinel（哨兵） 是 Redis 的一种运行模式 ，它主要的作用就是对 Redis 运行节点进行监控。当 master 节点出现故障的时候， Sentinel 会帮助我们实现故障转移，自动将某一台 slave 升级为 master，确保整个 Redis 系统的可用性。整个过程完全自动，不需要人工介入。</p><p>Sentinel 模式基于主从复制，只是多了一个 Sentinel 角色来帮助我们监控 Redis 节点的运行状态并自动实现故障转移。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206252012592.png" alt="image-20220625201233494" loading="lazy"></p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206171356063.png" alt="image-20220617135650963" loading="lazy"></p><p><strong>工作原理</strong>：</p><ul><li>每个 Sentinel 以每秒钟一次的频率向它所知道的 Master ， Slave 以及其他 Sentinel 实例发送一个 PING 命令。</li><li>如果一个实例距离最后一次有效回复 PING 命令的时间超过指定值， 则这个实例会被 Sentine 标记为主观下线。</li><li>如果一个 Master 被标记为主观下线，则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 是否真正进入主观下线状态。</li><li>当有足够数量的 Sentinel （大于等于配置文件指定值）在指定的时间范围内确认 Master 的确进入了主观下线状态， 则 Master 会被标记为客观下线 。若没有足够数量的 Sentinel 同意 Master已经下线， Master 的客观下线状态就会被解除。 若 Master 重新向 Sentinel 的 PING 命令返回有效回复， Master 的主观下线状态就会被移除。</li><li>哨兵节点会选举出哨兵 leader，负责故障转移的工作。</li><li>哨兵 leader 会推选出某个表现良好的从节点成为新的主节点，然后通知其他从节点更新主节点信息。</li></ul><p><strong>Sentinel 节点主要有下面这些作用</strong>：</p><ul><li>监控：监控所有 Redis 节点的状态是否正常。</li><li>通知 ：可以通过 API 通知系统管理员或其他计算机程序，其中一个受监控的 Redis 实例出现问题。</li><li>故障转移：如果一个 master 出现故障，Sentinel 会帮助我们实现故障转移，自动将某一台 slave 升级为 master，确保整个 Redis 系统的可用性。</li></ul><p>Redis Sentinel 本身设计的就是一个<strong>分布式系统</strong>，建议多个 Sentinel 节点协作运行。这样做的好处是：</p><ul><li>多个 Sentinel 节点通过投票的方式来确定 Sentinel 节点是否真的不可用，避免误判（比如网络问题可能会导致误判）。</li><li>Sentinel 自身就是高可用。</li></ul><p>Sentinel 也是一个 Redis 进程，只是不对外提供读写服务，通常建议哨兵配置成单数（大于等于 3 台）</p><p>在这些 Sentinel 中会有一个 Leader 的角色来负责故障转移。</p><p><strong>如何选择出 Leader 角色呢</strong>？ 这就需要用到分布式领域的共识算法 了。简单来说，共识算法就是让分布式系统中的节点就一个问题达成共识。在 Sentinel 选举 Leader 这个场景下，这些 Sentinel 要达成的共识就是谁才是 Leader 。</p><ul><li>大部分共识算法都是基于 Paxos 算法改进而来，在 Sentinel 选举 Leader 这个场景下使用的是 <a href="https://javaguide.cn/distributed-system/theorem&amp;algorithm&amp;protocol/raft-algorithm.html" target="_blank" rel="noopener noreferrer">Raft 算法<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a>。这是一个比 Paxos 算法更易理解和实现的共识算法—Raft 算法。更具体点来说，Raft 是 Multi-Paxos 的一个变种，其简化了 Multi-Paxos 的思想，变得更容易被理解以及工程实现。</li></ul><p>简单总结一下主从复制这种方案的优缺点：</p><ul><li>优点 ：<strong>支持故障自动切换</strong>。</li><li>缺点 ：部署相对麻烦且需要耗费更多的服务器资源，不支持横向扩展无法缓解写压力和存储压力。</li></ul><h3 id="_4、redis-cluster-分片-解决写压力太大或者缓存数据量太大" tabindex="-1"><a class="header-anchor" href="#_4、redis-cluster-分片-解决写压力太大或者缓存数据量太大" aria-hidden="true">#</a> 4、Redis Cluster（分片，解决写压力太大或者缓存数据量太大）</h3><blockquote><p>主从复制和 Redis Sentinel 方案本质都是通过增加副本的方式提高了 Redis 服务的可用性和读吞吐量。这两种方案都不支持横向扩展来缓解写压力以及解决缓存数据量过大的问题。</p><p>哨兵模式解决了主从复制不能自动故障转移、达不到高可用的问题，但还是存在主节点的写能力、容量受限于单机配置的问题。而cluster模式实现了Redis的分布式存储，每个节点存储不同的内容，解决主节点的写能力、容量受限于单机配置的问题。</p></blockquote><p><strong>方案</strong>：Redis cluster集群节点<strong>最小配置6个节点以上（3主3从）</strong>，其中主节点提供读写操作，从节点作为备用节点，不提供请求，只作为故障转移使用。</p><p><strong>什么是 Redis 切片集群</strong>？</p><ul><li>简单来说，就是部署多台 Redis 实例，这些 Redis 示例之间平等，并没有主从之说，它们同时对外提供读/写服务，缓存的数据库相对均匀地分布在这些 Redis 实例上。Redis 切片集群对于横向扩展非常友好，只需要增加 Redis 实例到集群中即可。</li></ul><p><strong>Redis 切片集群应该怎么做呢</strong>？ 在 Redis 3.0 之前，我们通常使用的是 Twemproxy、Codis 等分片集群方案。到了 Redis 3.0 的时候，Redis 官方推出了分片集群解决方案 - <a href="https://redis.io/topics/cluster-tutorial" target="_blank" rel="noopener noreferrer">Redis Cluster<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span></a> 。</p><p>Redis Cluster 通过分片（sharding）来进行数据管理，提供复制和故障转移等功能，可以非常方便地帮助我们解决 Redis 大数据量缓存的问题。并且，这种方案可以很方便地进行横向拓展（增加 Redis 机器）。</p><p>Redis Cluster 并没有使用一致性哈希，采用的是<strong>虚拟槽分区</strong> ，每一个键值对都属于一个 hash slot（ 虚拟槽）所有的键根据哈希函数映射到0～16383个整数槽内，每个节点负责维护一部分槽以及槽所映射的键值数据。</p><p><img src="https://pictured-bed.oss-cn-beijing.aliyuncs.com/img/2022/6/202206252016373.png" alt="image-20220625201631301" loading="lazy"></p><p><strong>哈希槽是如何映射到 Redis 实例上的</strong>？</p><ol><li>对键值对的 key 使用 crc16 算法计算一个结果</li><li>将结果对 16384 取余，得到的值表示 key 对应的哈希槽</li><li>根据该槽信息定位到对应的实例</li></ol><p>Redis Cluster 通常有 16384 hash slots ，要计算给定 key 的 hash slot ，我们只需要通过对每个 key 计算 CRC16 值，然后对 16384(hash slot 的数量) 取模。</p><div class="language-bash ext-sh line-numbers-mode"><pre class="language-bash"><code>slot <span class="token operator">=</span> crc16<span class="token punctuation">(</span><span class="token string">&quot;foo&quot;</span><span class="token punctuation">)</span> mod NUMER_SLOTS
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>假设集群有 3 个 Redis 节点组成，每个节点负责整个集群的一部分数据：</p><ul><li>Node 1 ： 0 - 5500 的 hash slot.</li><li>Node 2 ： 5501 - 11000 的 hash slot. .</li><li>Node 3 ： 11001 - 16383 的 hash slot..</li></ul><p>Redis Cluster 是<strong>去中心化</strong>的，任何一台机器宕机，另外两个节点，不影响的。因为 key 找的是 hash slot，不是机器。并且，我们想要添加新的节点比如 Node4 进入 Redis Cluster 也非常方便，只需要将一些 hash slot 从 节点 Node1，Node2，Node3 移动到 Node4 即可。</p><p>简单总结一下 Redis Cluster 这种方案的优缺点：</p><ul><li>优点 ： <ul><li><strong>无中心架构</strong>，支持动态扩容；</li><li>数据按照 slot 存储分布在多个节点，节点间数据共享，可<strong>动态调整数据分布</strong>。</li><li>解耦了数据和节点之间的关系，提升了集群的横向扩展性和容错性，可以<strong>横向扩展缓解写压力和存储压力，具备故障转移功能</strong>。</li><li><strong>高可用性</strong>。部分节点不可用时，集群仍可用。集群模式能够实现自动故障转移（failover），节点之间通过 <code>gossip</code> 协议交换状态信息，用<strong>投票机制</strong>完成<code> Slave</code> 到 <code>Master</code> 的角色转换。</li></ul></li><li>缺点 ： <ul><li>不支持批量操作（pipeline）。</li><li>数据通过异步复制，<strong>不保证数据的强一致性</strong>。</li><li>事务操作支持有限，只支持多 key 在同一节点上的事务操作，当多个 key 分布于不同的节点上时无法使用事务功能。</li><li>key 作为数据分区的最小粒度，不能将一个很大的键值对象如 hash 、 list 等映射到不同的节点。</li><li><strong>不支持多数据库空间</strong>，单机下的Redis可以支持到16个数据库，集群模式下只能使用1个数据库空间。</li></ul></li></ul><blockquote><p>采用协议</p></blockquote><p>Redis Cluster 是一个典型的分布式系统，分布式系统中的各个节点需要互相通信。既然要相互通信就要遵循一致的通信协议，<strong>Redis Cluster 中的各个节点基于 Gossip 协议 来实现数据的最终一致性</strong>（每个 Redis 节点都维护了一份集群的状态信息）。</p><p>Gossip 协议是分布式系统中非常重要的一个协议，我的建议是学有余力的小伙伴尽量要搞懂。不过，这个在面试中问的也不多，除非是你在简历上写了自己熟悉 Gossip 协议。关于 Gossip 协议的详细解读，推荐你看看下面这两篇文章：</p><p>Redis Cluster 各个节点通信主要交换的信息包括：</p><p>1、myslots ：当前节点所负责的 slots 信息 2、flags : 当前节点当前的状态 3......</p><h4 id="哈希分区算法有哪些" tabindex="-1"><a class="header-anchor" href="#哈希分区算法有哪些" aria-hidden="true">#</a> 哈希分区算法有哪些？</h4><p><strong>节点取余分区</strong>：使用特定的数据，如Redis的键或用户ID，对节点数量N取余：hash（key）%N计算出哈希值，用来决定数据映射到哪一个节点上。 优点是简单性。扩容时通常采用翻倍扩容，避免数据映射全部被打乱导致全量迁移的情况。</p><p><strong>一致性哈希分区</strong>：为系统中每个节点分配一个token，范围一般在0~232，这些token构成一个哈希环。数据读写执行节点查找操作时，先根据key计算hash值，然后顺时针找到第一个大于等于该哈希值的token节点。 这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点，对其他节点无影响。</p><p><strong>虚拟槽分区</strong>：所有的键根据哈希函数映射到0~16383整数槽内，计算公式：slot=CRC16（key）&amp;16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。<strong>Redis Cluser采用虚拟槽分区算法</strong>。</p><hr><h2 id="_12、场景题" tabindex="-1"><a class="header-anchor" href="#_12、场景题" aria-hidden="true">#</a> 12、场景题</h2><h3 id="_1、mysql-里有-2000w-数据-redis-中只存-20w-的数据-如何保证-redis-中的数据都是热点数据" tabindex="-1"><a class="header-anchor" href="#_1、mysql-里有-2000w-数据-redis-中只存-20w-的数据-如何保证-redis-中的数据都是热点数据" aria-hidden="true">#</a> 1、MySQL 里有 2000w 数据，redis 中只存 20w 的数据，如何保证 redis 中的数据都是热点数据？</h3><p>Redis 内存数据集大小上升到一定大小的时候，就会施行数据淘汰策略。</p><p>Redis 提供 6 种数据淘汰策略：</p><ol><li><strong>volatile-lru（least recently used）</strong>：从已设置过期时间的数据集（server.db[i].expires）中挑选最近最少使用的数据淘汰</li><li><strong>volatile-ttl</strong>：从已设置过期时间的数据集（server.db[i].expires）中挑选将要过期的数据淘汰</li><li><strong>volatile-random</strong>：从已设置过期时间的数据集（server.db[i].expires）中任意选择数据淘汰</li><li><strong>allkeys-lru（least recently used）</strong>：当内存不足以容纳新写入数据时，在键空间中，移除最近最少使用的 key（这个是最常用的）</li><li><strong>allkeys-random</strong>：从数据集（server.db[i].dict）中任意选择数据淘汰</li><li><strong>no-eviction</strong>：禁止驱逐数据，也就是说当内存不足以容纳新写入数据时，新写入操作会报错。这个应该没人使用吧！</li></ol><p>4.0 版本后增加以下两种：</p><ol><li><strong>volatile-lfu（least frequently used）</strong>：从已设置过期时间的数据集（server.db[i].expires）中挑选最不经常使用的数据淘汰</li><li><strong>allkeys-lfu（least frequently used）</strong>：当内存不足以容纳新写入数据时，在键空间中，移除最不经常使用的 key</li></ol><h3 id="_2、如-redis-里面有-1-亿个-key-其中有-10w-个-key-是以某个固定的已知的前缀开头的-如果将它们全部找出来" tabindex="-1"><a class="header-anchor" href="#_2、如-redis-里面有-1-亿个-key-其中有-10w-个-key-是以某个固定的已知的前缀开头的-如果将它们全部找出来" aria-hidden="true">#</a> 2、如 Redis 里面有 1 亿个 key，其中有 10w 个 key 是以某个固定的已知的前缀开头的，如果将它们全部找出来？</h3><p>使用 keys 指令可以扫出指定模式的 key 列表。</p><p><strong>对方接着追问：如果这个 redis 正在给线上的业务提供服务，那使用 keys 指令会有什么问题？</strong></p><p>这个时候你要回答 redis 关键的一个特性：redis 的单线程的。keys 指令会导致线程阻塞一段时间，线上服务会停顿，直到指令执行完毕，服务才能恢复。这个时候可以使用<code>scan</code>指令，scan 指令可以无阻塞的提取出指定模式的 key 列表，但是会有一定的重复概率，在客户端做一次去重就可以了，但是整体所花费的时间会比直接用 keys 指令长。</p><h3 id="_3、一个-redis-实例最多能存放多少的-keys-list、set、sorted-set-他们最多能存放多少元素" tabindex="-1"><a class="header-anchor" href="#_3、一个-redis-实例最多能存放多少的-keys-list、set、sorted-set-他们最多能存放多少元素" aria-hidden="true">#</a> 3、一个 Redis 实例最多能存放多少的 keys？List、Set、Sorted Set 他们最多能存放多少元素？</h3><p>理论上 Redis 可以处理多达 232 的 keys，并且在实际中进行了测试，每个实例至少存放了 2 亿 5 千万的 keys。我们正在测试一些较大的值。任何 list、set、和 sorted set 都可以放 232 个元素。换句话说，Redis 的存储极限是系统中的可用内存值。</p><hr><p>整理者：长路 时间：2022.6.17</p></div><!----><footer class="page-meta"><div class="meta-item edit-link"><a href="https://gitee.com/changluJava/personal-reading/edit/main/demo/src/zh/interview/Redis.md" rel="noopener noreferrer" target="_blank" aria-label="编辑此页" class="nav-link label"><!--[--><svg xmlns="http://www.w3.org/2000/svg" class="icon edit-icon" viewbox="0 0 1024 1024" fill="currentColor" aria-label="edit icon"><path d="M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"></path><path d="M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"></path></svg><!--]-->编辑此页<span><svg class="external-link-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewbox="0 0 100 100" width="15" height="15"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg><span class="external-link-icon-sr-only">open in new window</span></span><!----></a></div><!----><!----></footer><nav class="page-nav"><a href="/zh/interview/NIO%E4%B8%8ENetty.html" class="nav-link prev" aria-label="Netty"><div class="hint"><span class="arrow left"></span>上一页</div><div class="link"><span class="icon iconfont icon-lock" style=""></span>Netty</div></a><a href="/zh/interview/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6.html" class="nav-link next" aria-label="主流框架"><div class="hint">下一页<span class="arrow right"></span></div><div class="link">主流框架<span class="icon iconfont icon-lock" style=""></span></div></a></nav><div class="giscus-wrapper input-top" style="display:block;"><div style="text-align:center">Loading...</div></div><!----><!--]--></main><!--]--><footer class="footer-wrapper"><div class="footer">默认页脚</div><div class="copyright">Copyright © 2022 长路</div></footer><!--]--></div><!--]--><!----><!--]--></div>
    <script type="module" src="/assets/app.e732cc94.js" defer></script>
  </body>
</html>
